diff options
| author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
|---|---|---|
| committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
| commit | 429d5f88b94ff28416cbfc6420b6389fa284df97 (patch) | |
| tree | fb6fdaf7731de1ef396f98b748c56f3149801c84 | |
Import RTFL 0.1.1v0.1.1
187 files changed, 61258 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e01432e --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +config.* +autom4te.cache +Makefile +Makefile.in +configure +compile +build +aclocal.m4 +depcomp +install-sh +stamp-h1 +missing @@ -0,0 +1,3 @@ +Author of RTFL is Sebastian Geerken (sgeerken-at-dillo.org). Parts are +copied from dillo, namely "lout" and "dw", whose main author is also +Sebastian Geerken. @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 3 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, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU 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. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..1f2d56c --- /dev/null +++ b/ChangeLog @@ -0,0 +1,149 @@ +Version 0.1.1 (Jul 31 2016) +--------------------------- +General changes: +- New "general" protocol module, with one command, "time". +- Programs always read a file ".rtfl" in the current directory. +- New program "rtfl-objbase". + +Rtfl-objview: +- Some work on Graph2 (updated to Graphviz version 2.38.0, fixed rendering bug, + draw edges as b-splines, better library detection by configure script). It is + now the default. +- Ids of deleted objects are now forgotten. +- "obj-create" is now navigable. +- New option "-b"/"-B". +- Fixed bug when displaying stack trace. + +Miscellaneous: +- Scripts now honour optional return values of "obj-leave". + +New: +- Started work on JVM agent, which automates many RTFL messages. + +Internal Changes: +- ObjectsController (and implementations), LinesSource, LinesSink. + + +Version 0.1 (Feb 05 2015) +------------------------- +General changes: +- Versioning of protocol modules. +- Quoting in the protocol, so that a literal ':' is possible. +- Macros for "obj-mark", DBG_OBJ_MARK and DBG_OBJ_MARKF. +- "obj-leave" now supports optional return values; new macro DBG_OBJ_LEAVE_VAL. + +Rtfl-objview: +- Class patterns in "obj-class-color" are now sorted by specifity. +- Fixed redrawing and scrolling problem. +- Fixed sorting of attributes. +- "Switch between related" now also supports "obj-msg-start" and "obj-msg-end". +- Attributes with no children (sub-attributes or "obj-set" commands) are now + hidden. +- Colorful stacktrace. + +Internal changes: +- Updated to newer version of dillo widget (revision 3948:0769a58d63f9 + from <http://hg.dillo.org/dillo>). +- The ascent of VBox is now the ascent of the first *visible* child. + + +Version 0.0.9 (Oct 26 2014) +--------------------------- +- New scripts "rtfl-stacktraces" and "rtfl-objfilter". +- New filters "rtfl-findrepeat" and "rtfl-tee". +- Updated other scripts: "obj-enter" and "obj-leave" are now regarded. +- Options "-a" and "-A" for "rtfl-objtail". +- Fixed parser bug. +- New command "obj-mark". +- New command "obj-object-color"; renamed "obj-color" to "obj-class-color". +- Updated parser of rtfl-objcount (eventually by the common parser). +- DBG_OBJ_COLOR: order of arguments has changed from (color, class) to + (class, color). +- Rtfl-objview: Experimental graph widget based on Graphviz ("./configure + --enable-graph2"). +- Rtfl-objview: Attributes are sorted alphabetically on the first level. + +Internal changes: +- General method Container::size() in lout::container. +- Methods "equal" and "hashValue" work now partly for containers. +- Common parser for objects module. + + +Version 0.0.8 (Jul 20 2014) +--------------------------- +- Scripts are now part of the RTFL package. +- New commands "obj-enter" and "obj-leave" (and respective macros) +- "Show strack trace" in rtfl-objview. +- "Switch between related" commands (currently "obj-enter" and "obj-leave") + + +Version 0.0.7 (May 26 2014) +--------------------------- +- Renamed "debug.hh" to "debug_rtfl.hh" and moved it to root directory. +- New macros DBG_OBJ_ARRATTRSET_*. +- "obj-msg-start" and "obj-msg-end" add messages (hidden by default). +- All navigable commands can be hidden, selectable by type. +- Hide all/show all commands; view code of command. +- Command line arguments. +- Corrected macros (DBG_OBJ_MSG_START and DBG_OBJ_MSG_END). +- Tests now in two versions: with and without RTFL messages active. +- Fixed (or worked around) overflow error in graph widget (arrow heads). +- Fixed overflow error in hbox and vbox widget. +- Fixed some memory problems. + + +Version 0.0.6 (Dec 29 2013) +--------------------------- +- "\n" at the beginning of RTFL messages. +- Fixed a bug introduced with 0.0.5 (indented messages). + + +Version 0.0.5 (Dec 26 2013) +--------------------------- +Important note: "rtfl-objects" has been renamed to "rtfl-objview", so you +should delete the old "rtfl-objects" from the target directory (typically +/usr/local/bin). + +Furthermore: +- UI changes (menu bar). +- Selectable and navigable command. +- Filtering of "obj-msg". +- New protocol command "obj-delete". +- Fixed a bug related to "obj-ident". +- New viewer "rtfl-objcount". + +Internal changes: +- Fixed bug in dw related to inverse backgrounds. +- Fixed handling zero size children in rtfl::dw::HBox and rtfl::dw::VBox. +- Added link handling in rtfl::dw::Label. + + +Version 0.0.4 (Dec 12 2013) +--------------------------- +- Fixed a bug (parser) introduced by 0.0.3. + + +Version 0.0.3 (Dec 12 2013) +--------------------------- +- Numbers are preceeded to some commands. +- Fixed two I/O related bugs (buffered reading of commands, cpu hogging). + +Internal changes: +- Made parser more robust (should not crash anymore). + + +Version 0.0.2 (Dec 09 2013) +--------------------------- +- Fixed some flaws in tests/debug.hh. +- Implemented "obj-color". +- New command "obj-ident" and macro DBG_OBJ_BASECLASS. +- Documentation "rtfl.html". + +Internal changes: +- Some work on widgets (rtfl::dw): removeChild, registration of class names. +- ObjectsGraph: list of commands, which can be undone (partly incomplete). + + +Version 0.0.1 (Dec 01 2013) +--------------------------- +- Initial release. @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell command `./configure && make && make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `<wchar.h>' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..acee8ce --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = lout common dw dwr objects scripts tests doc + +if HAS_JAVA + SUBDIRS += java +endif + +ACLOCAL_AMFLAGS=-I m4 + +# "debug_rtfl.hh" is generated from "debug_rtfl.hh.in". When calling +# "make", the subdirectories are processed before "debug_rtfl.hh" is +# generated, so it may be that you have to generate it explicitly by +# calling "make debug_rtfl.hh". + +# For convenience, "debug_rtfl.hh" is included into the distribution. +# Furthermore (mainly because of the problem mentioned above), it is +# also added to the SVN repository. + +EXTRA_DIST = debug_rtfl.hh debug_rtfl.hh.in + +debug_rtfl.hh: debug_rtfl.hh.in create-debug_rtfl-hh + ./create-debug_rtfl-hh < debug_rtfl.hh.in > debug_rtfl.hh @@ -0,0 +1 @@ +See ChangeLog. @@ -0,0 +1,149 @@ +Overview +-------- +RTFL, which stands for "Read The Figurative Logfile", is a both a +protocol for structured debug messages, as well as a collection of +programs (currently two) displaying these debug messages in a +semi-graphical way, so that it becomes simpler to determine what the +debugged program does. + +Programs are prepared to print these special debug messages to +standard output, which are then passed to a viewer program like +"rtfl-objcount" or "rtfl-objview". + +See "doc/rtfl.html" for a comprehensive description. + + +Copyright +--------- +RTFL is free software, released under the GPL version 3 or later (see +COPYING for details), with the following exception: + +The copyright holders of RTFL give you permission to link the program +rtfl-objview statically or dynamically against all versions of the +graphviz library, which are published by AT&T Corp. under one of the +following licenses: + +- Common Public License version 1.0 as published by International + Business Machines Corporation (IBM), or +- Eclipse Public License version 1.0 as published by the Eclipse + Foundation. + +Both the protocol and the header file "debug_rtfl.hh", which provides +macros for the protocol, are not protected at all, but in the public +domain. This means that are no restrictions for the programs being +debugged using RTFL. + +Some (informal) notes about the exception: + +1. Graphviz, which is used by the Graph2 widget, is published under + the Eclipse Public License version 1.0, older versions are + published under the Common Public License version 1.0. Both + licenses are free software licenses, but weak copyleft licenses, + and so incompatible with any version the GNU General Public + License. See [1] and [2] for details. The license exception solves + this. + +2. Since the CPL and EPL are *weak* copyleft licenses, there is no + problem in the other direction. + +3. All parts of RTFL are still compatible with the GNU GPL. + +4. I want to restrict this license exception to free libraries; this + is the reason that the CPL and the EPL are explicitly mentioned. If + you have some "technical" problems with this limitation, feel free + to contact me (see below). + + +Building +-------- +As usual: "./configure && make && make install" (or "install-strip"); +see "INSTALL" for details. If you are using the version from SVN +instead of the release tarball, you have to run "libtoolize && aclocal +&& autoconf && automake" before. + +You need FLTK version 1.3 (try "fltk-config --version"). Get it from +<http://fltk.org/software.php>, or install a package suitable for your +operating system (on Debian: "apt-get install libfltk1.3-dev"). + +There is a widget, Graph2, which enhances the graph layouting of +rtfl-objview, as compared to the older Graph widget, and it has +matured enough to become the default. It depends on the Graphviz +library (see <http://www.graphviz.org/Download.php> or install a +suitable package, e. g. "libgraphviz-dev" on Debian), version 2.38.0 +or later. If you use an older version, try to modify "configure.ac"; +if you succeed, drop me a note. + +If Graphviz is not installed, the old Graph widget is used. If you +want to use the old Graph widget in any case, run "./configure" with +"--disable-graph2" to use the old widget. + +Furthermore, there has been some work on a Java VM agent, which +automates generation of RTFL messages by Java programs. The JDK is +searched according to following rules: + + (i) If "--disable-java" is passed to "./configure", the JVM agent is + not build at all. + (ii) The root of the JDK (under which "bin", "include" etc. are + found) may be passed explicitly by "--with-java-home=...". +(iii) Otherwise, "javac" is found in the path, its symbolic links are + followed, and so the root of the JDK is searched. + +Of course, if "javac" is not found, or some crucial files within the +JDK are missing, the agent is neither build. + +See java/README for more details. + + +Hacking +------- +RTFL uses parts of the dillo widget from the dillo web browser +(<http://www.dillo.org/>); see "lout" and "dw" directory. The current +version was taken on Oct 27 2014 from the repository +<http://hg.dillo.org/dillo>, revision 3948:0769a58d63f9. Few changes +are generally made: + +- the parts of "libDw-widgets.a" are removed, they are not needed in + RTFL; + +- dw::fltk::ui:ComplexButtonResource and related classes and files + (FltkFlatView and ComplexButton) are removed, since they are not + needed, and (this is actually the main reason) the copyright of + ComplexButton is a bit unclear [3]; + +- the copyright notices are modified by adding the license exception + (this is automated with the script "update_copyright" which is part + of the SVN repository, albeit not release tarball). + +Smaller changes to "lout" and "dw" can be made within RTFL; from time +to time, these changes should be back-ported, before a new version of +"lout" and "dw" is copied to RTFL. + +The directory "dwr" provides some general dillo widgets used in RTFL, +and "common" base code for all viewers and protocol modules. Specific +viewers and protocol modules have there own directory (currently only +"objects": may become subject to change.) + + +Future +------ +First of all, there are numerous bugs, flaws, and things to +improve. (No list yet.) + +For more, see the file <http://home.gna.org/rtfl/future.html". + + +Contact +------- +Write to Sebastian Geerken <sgeerken-at-dillo.org>. See +<http://home.gna.org/rtfl/> for news. + + +Footnotes +--------- +[1] http://www.gnu.org/philosophy/license-list.html#CommonPublicLicense10 + +[2] http://www.gnu.org/philosophy/license-list.html#EPL + +[3] Nothing serious; ComplexButton is a derivate of the Button widget + of FLTK, which is released under the GNU LGPL, so that linking + with the graphviz library should not cause a problem. diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..b8e1313 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,30 @@ +# Notes about libraries: "librtfl-tools.a" contains everything not +# depending on FLTK, which can so be used in command line tools; +# "librtfl-common.a" depens on FLTK, and also on the former. + +AM_CPPFLAGS = \ + -I$(top_srcdir) + +noinst_LIBRARIES = librtfl-common.a librtfl-tools.a + +bin_PROGRAMS = rtfl-findrepeat rtfl-tee + +librtfl_common_a_SOURCES = \ + about.hh \ + about.cc \ + fltk_lines.hh \ + fltk_lines.cc + +librtfl_tools_a_SOURCES = \ + lines.hh \ + lines.cc \ + parser.hh \ + parser.cc \ + tools.hh \ + tools.cc + +rtfl_findrepeat_SOURCES = rtfl_findrepeat.cc + +rtfl_findrepeat_LDADD = librtfl-tools.a ../lout/liblout.a + +rtfl_tee_SOURCES = rtfl_tee.c diff --git a/common/about.cc b/common/about.cc new file mode 100644 index 0000000..01ad3ac --- /dev/null +++ b/common/about.cc @@ -0,0 +1,109 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "about.hh" + +#include "config.h" + +#include <FL/Fl_Return_Button.H> +#include <FL/Fl_Box.H> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +namespace rtfl { + +namespace common { + +void AboutWindow::close (Fl_Widget *widget, void *data) +{ + ((AboutWindow*)data)->hide (); +} + + +AboutWindow::AboutWindow (const char *prgName, const char *licenceException, + int height) : + Fl_Window (WIDTH, height, "") +{ + + const char *titleFmt = "RTFL: About %s"; + const char *textFmt = + "%s " VERSION "\n" + "\n" + "%s is part of RTFL (Read The Figurative Logfile).\n" + "\n" + "Copyright 2013-2015 Sebastian Geerken <sgeerken@@dillo.org>\n" + "\n" + "RTFL 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 3 of the License, or (at your " + "option) any later version%s.\n" + "\n" + "With RTFL comes some documentation, see “doc/rtfl.html” in the " + "tarball. For more informations, updates etc. see " + "<http://home.gna.org/rtfl/>."; + + int titleLen = strlen (titleFmt) - 2 + strlen (prgName) + 1; + title = new char [titleLen]; + snprintf (title, titleLen, titleFmt, prgName); + label (title); + + char *capName = strdup (prgName); + capName[0] = toupper (capName[0]); + int textLen = + strlen (textFmt) - 2 + strlen (prgName) - 2 + strlen (capName) + 1 + - 2 + strlen (licenceException); + text = new char[textLen]; + snprintf (text, textLen, textFmt, prgName, capName, licenceException); + free (capName); + + Fl_Box *textWidget = + new Fl_Box(SPACE, SPACE, WIDTH - 2 * SPACE, + height - 3 * SPACE - BUTTON_HEIGHT, text); + textWidget->box(FL_NO_BOX); + textWidget->align(FL_ALIGN_WRAP); + + Fl_Return_Button *close = + new Fl_Return_Button(WIDTH - BUTTON_WIDTH - SPACE, + height - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH, + BUTTON_HEIGHT, "Close"); + close->callback (AboutWindow::close, this); +} + +AboutWindow::~AboutWindow () +{ + delete[] title; + delete[] text; +} + +} // namespace common + +} // namespace rtfl diff --git a/common/about.hh b/common/about.hh new file mode 100644 index 0000000..2a46483 --- /dev/null +++ b/common/about.hh @@ -0,0 +1,30 @@ +#ifndef __COMMON_ABOUT_HH__ +#define __COMMON_ABOUT_HH__ + +#include <FL/Fl_Window.H> + +namespace rtfl { + +namespace common { + +class AboutWindow: public Fl_Window +{ +private: + char *title, *text; + + static void close (Fl_Widget *widget, void *data); + + enum { WIDTH = 450, BUTTON_WIDTH = 80, BUTTON_HEIGHT = 25, SPACE = 10 }; + +public: + enum { HEIGHT_SIMPLE = 300, HEIGHT_EXCEPTION = 480 }; + + AboutWindow (const char *prgName, const char *licenceException, int height); + ~AboutWindow (); +}; + +} // namespace common + +} // namespace rtfl + +#endif // __COMMON_ABOUT_HH__ diff --git a/common/fltk_lines.cc b/common/fltk_lines.cc new file mode 100644 index 0000000..5bf4b00 --- /dev/null +++ b/common/fltk_lines.cc @@ -0,0 +1,140 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "fltk_lines.hh" + +#include <stdio.h> +#include <fcntl.h> +#include <Fl/Fl.H> + +using namespace lout::container::typed; + +namespace rtfl { + +namespace common { + +// ------------------------- +// FltkLinesSource +// ------------------------- + +FltkLinesSource::TimeoutInfo::TimeoutInfo (FltkLinesSource *source, int type) +{ + this->source = source; + this->type = type; +} + +FltkLinesSource::FltkLinesSource () +{ + timeoutInfos = new List<TimeoutInfo> (true); +} + +FltkLinesSource::~FltkLinesSource () +{ + delete timeoutInfos; +} + +void FltkLinesSource::staticProcessInputCallback (int fd, void *data) +{ + ((FltkLinesSource*)data)->processInputCallback (fd); +} + +void FltkLinesSource::processInputCallback (int fd) +{ + int n = processInput (fd); + + if (n == 0) { + // We read non-blocking, so -1 is returned and (errno set to + // EAGAIN) when no data is currently available. When 0 is + // returned, this means that there is permanently no data + // (typically that the tested program has terminated). For some + // reasons, the cpu is hogged then; this is avoided by removing + // the read function again. + Fl::remove_fd(0, FL_READ); + getSink()->finish (); + } +} + +void FltkLinesSource::setup (tools::LinesSink *sink) +{ + setSink (sink); + + int flags = fcntl(0, F_GETFL, 0); + fcntl(0, F_SETFL, flags | O_NONBLOCK); + + Fl::add_fd(0, FL_READ, staticProcessInputCallback, (void*)this); +} + +void FltkLinesSource::addTimeout (double secs, int type) +{ + TimeoutInfo *timeoutInfo = new TimeoutInfo (this, type); + timeoutInfos->append (timeoutInfo); + Fl::add_timeout(secs, timeoutCallback, timeoutInfo); +} + +void FltkLinesSource::timeoutCallback (void *data) +{ + TimeoutInfo *timeoutInfo = (TimeoutInfo*) data; + timeoutInfo->getSource()->getSink()->timeout (timeoutInfo->getType ()); + timeoutInfo->getSource()->timeoutInfos->removeRef (timeoutInfo); +} + +void FltkLinesSource::removeTimeout (int type) +{ + // Iterators will not work when the set is modified; hence this nested loop. + bool found; + do { + found = false; + for (Iterator<TimeoutInfo> it = timeoutInfos->iterator (); + !found && it.hasNext (); ) { + TimeoutInfo *timeout = it.getNext(); + if (timeout->getType () == type) { + found = true; + Fl::remove_timeout(timeoutCallback, timeout); + timeoutInfos->removeRef (timeout); + } + } + } while (found); +} + +// --------------------------- +// FltkDefaultSource +// --------------------------- + +FltkDefaultSource::FltkDefaultSource (): LinesSourceSequence (true) +{ + int fd = open (".rtfl", O_RDONLY); + if (fd != -1) + add (new tools::BlockingLinesSource (fd)); + + add (new FltkLinesSource ()); +} + +} // namespace objects + +} // namespace rtfl diff --git a/common/fltk_lines.hh b/common/fltk_lines.hh new file mode 100644 index 0000000..424ab82 --- /dev/null +++ b/common/fltk_lines.hh @@ -0,0 +1,52 @@ +#ifndef __COMMON_FLTK_LINES_HH__ +#define __COMMON_FLTK_LINES_HH__ + +#include "lines.hh" + +namespace rtfl { + +namespace common { + +class FltkLinesSource: public tools::FileLinesSource +{ + class TimeoutInfo: public lout::object::Object + { + private: + FltkLinesSource *source; + int type; + + public: + TimeoutInfo (FltkLinesSource *source, int type); + + inline FltkLinesSource *getSource () { return source; } + inline int getType () { return type; } + }; + + lout::container::typed::List<TimeoutInfo> *timeoutInfos; + + static void staticProcessInputCallback (int fd, void *data); + static void timeoutCallback (void *data); + void processInputCallback (int fd); + +public: + FltkLinesSource (); + ~FltkLinesSource (); + + void setup (tools::LinesSink *sink); + void addTimeout (double secs, int type); + void removeTimeout (int type); +}; + + +class FltkDefaultSource: public tools::LinesSourceSequence +{ +public: + FltkDefaultSource (); +}; + + +} // namespace common + +} // namespace rtfl + +#endif // __COMMON_FLTK_LINES_HH__ diff --git a/common/lines.cc b/common/lines.cc new file mode 100644 index 0000000..e6fa8c2 --- /dev/null +++ b/common/lines.cc @@ -0,0 +1,362 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "lines.hh" +#include "tools.hh" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/select.h> +#include <sys/timeb.h> + +#if 0 +# define PRINT(fmt) printf ("---- [%p] " fmt "\n", this) +# define PRINTF(fmt, ...) printf ("---- [%p] " fmt "\n", this, __VA_ARGS__) +#else +# define PRINT(fmt) +# define PRINTF(fmt, ...) +#endif + +using namespace lout::container::typed; +using namespace lout::misc; + +namespace rtfl { + +namespace tools { + +// ----------------------------- +// LinesSourceSequence +// ----------------------------- + +LinesSourceSequence::VirtualSink::VirtualSink () +{ +} + +void LinesSourceSequence::VirtualSink::processLine (char *line) +{ + sequence->sink->processLine (line); +} + +void LinesSourceSequence::VirtualSink::setLinesSource (LinesSource *source) +{ +} + +void LinesSourceSequence::VirtualSink::finish () +{ + // If a child source calls sink->finish() within setup(), this is + // called recursively, but this does not cause problems. + + if (sequence->iterator.hasNext ()) { + LinesSource *source = sequence->iterator.getNext (); + source->setup (this); + } else { + sequence->sink->finish (); + } +} + +void LinesSourceSequence::VirtualSink::timeout (int type) +{ + sequence->sink->timeout (type); +} + +LinesSourceSequence::LinesSourceSequence (bool ownerOfSources) +{ + virtualSink.sequence = this; + sources = new List<LinesSource> (ownerOfSources); + setupCalled = false; +} + +LinesSourceSequence::~LinesSourceSequence () +{ + delete sources; +} + +void LinesSourceSequence::add (LinesSource *source) +{ + assert (!setupCalled); + sources->append (source); +} + +void LinesSourceSequence::setup (LinesSink *sink) +{ + this->sink = sink; + sink->setLinesSource (this); + setupCalled = true; + iterator = sources->iterator (); + virtualSink.finish (); +} + +void LinesSourceSequence::addTimeout (double secs, int type) +{ + // TODO: After calling this, no source should be added. + // TODO: Processed timeouts must be removed from other sources as well? + + // Sent to all, even if only one child source will actually trigger the + // timeout; but we do not know which one. + + // In the real world, LinesSourceSequence is used for ".rtfl" and stdin, so + // we do not have to worry too much about correctly handling timeouts. + + for (Iterator<LinesSource> it = sources->iterator (); it.hasNext (); ) { + it.getNext()->addTimeout (secs, type); + } +} + +void LinesSourceSequence::removeTimeout (int type) +{ + for (Iterator<LinesSource> it = sources->iterator (); it.hasNext (); ) { + it.getNext()->removeTimeout (type); + } +} + +// ------------------------- +// FileLinesSource +// ------------------------- + +FileLinesSource::FileLinesSource () +{ + bufPos = 0; + completeLine = true; +} + +int FileLinesSource::processInput (int fd) +{ + int n; + if ((n = read (fd, buf + bufPos, MAX_LINE_SIZE - bufPos)) > 0) { + int bytesAvail = bufPos + n; + int startOfLine = 0; + bool lineProcessed; + + //printf ("--> %d bytes read, %d available\n", n, bytesAvail); + + do { + lineProcessed = false; + for (int i = startOfLine; !lineProcessed && i < bytesAvail; i++) { + if (buf[i] == '\n') { + buf[i] = 0; + + // If lines are too long (see below, where completeLine + // is set to false), they are not processed. + if (completeLine) + sink->processLine (buf + startOfLine); + + lineProcessed = true; + startOfLine = i + 1; + + completeLine = true; + } + } + } while (lineProcessed); + + memmove (buf, buf + startOfLine, bytesAvail - startOfLine); + bufPos = bytesAvail - startOfLine; + + PRINTF ("processInput: %d bytes left in buffer", bufPos); + + // Handle case when line is to large (> MAX_LINE_SIZE + // bytes). The whole line is discarded (completeLine), so we + // empty the buffer by setting bufPos to 0. + if (bufPos == MAX_LINE_SIZE) { + bufPos = 0; + completeLine = false; + } + + //printf (" --> %d processed, new pos: %d; will read %d\n", + // startOfLine, bufPos, MAX_LINE_SIZE - bufPos); + } + + //printf (" --> read(2) returns %d\n", n); + + return n; +} + +// ----------------------------- +// BlockingLinesSource +// ----------------------------- + +BlockingLinesSource::TimeoutInfo::TimeoutInfo (long time, int type) +{ + this->time = time; + this->type = type; +} + +bool BlockingLinesSource::TimeoutInfo::equals(Object *other) +{ + return time == ((TimeoutInfo*)other)->time && + type == ((TimeoutInfo*)other)->type; +} + +int BlockingLinesSource::TimeoutInfo::hashValue() +{ + // This should better be hidden in lout::objects. Cf. Pointer::hashValue(). +#if SIZEOF_LONG == 4 + return (int)time ^ type; +#else + return ((intptr_t)time >> 32) ^ ((intptr_t)time) ^ type; +#endif +} + +BlockingLinesSource::BlockingLinesSource (int fd) +{ + this->fd = fd; + timeoutInfos = new HashSet<TimeoutInfo> (true); +} + +BlockingLinesSource::~BlockingLinesSource () +{ + delete timeoutInfos; +} + +void BlockingLinesSource::setup (LinesSink *sink) +{ + setSink (sink); + + // We read non-blocking so that select(2) will work properly. + // (FileLinesSource::processInput would block otherwise.) + int flags = fcntl(0, F_GETFL, 0); + fcntl(0, F_SETFL, flags | O_NONBLOCK); + + bool eos = false; + while (!eos) { + fd_set readfds; + FD_ZERO (&readfds); + FD_SET (fd, &readfds); + + TimeoutInfo *nextTimeout = getNextTimeoutInfo (); + + struct timeval tv, *tvp; + if (nextTimeout == NULL) { + tvp = NULL; + PRINT ("no timeout"); + } else { + long tdelta = max (nextTimeout->getTime () - getCurrentTime (), 0L); + tv.tv_sec = tdelta / 1000; + tv.tv_usec = (tdelta % 1000) * 1000; + tvp = &tv; + PRINTF ("waiting %ld (%ld, %ld)", tdelta, tv.tv_sec, tv.tv_usec); + } + + PRINT (">> processTimeouts"); + processTimeouts (); + PRINT ("<< processTimeouts"); + + PRINT (">> select"); + if (select (fd + 1, &readfds, NULL, NULL, tvp) == -1) + syserr ("select failed"); + PRINT ("<< select"); + + processTimeouts (); + + if (FD_ISSET (fd, &readfds)) { + PRINT (">> processInput"); + int n = processInput (fd); + PRINT ("<< processInput"); + if (n == 0) { + eos = true; + } + } + } + + close (fd); + sink->finish (); +} + +void BlockingLinesSource::addTimeout (double secs, int type) +{ + PRINTF ("addTimeout (%g, %d)", secs, type); + timeoutInfos->put (new TimeoutInfo (getCurrentTime () + secs * 1000, type)); +} + +void BlockingLinesSource::removeTimeout (int type) +{ + PRINTF ("removeTimeout (%d)", type); + + // Iterators will not work when the set is modified; hence this nested loop. + bool found; + do { + found = false; + for (Iterator<TimeoutInfo> it = timeoutInfos->iterator (); + !found && it.hasNext (); ) { + TimeoutInfo *timeout = it.getNext(); + if (timeout->getType () == type) { + found = true; + timeoutInfos->remove (timeout); + } + } + } while (found); +} + +long BlockingLinesSource::getCurrentTime () +{ + struct timeb t; + if (ftime (&t) == -1) + syserr ("ftime() failed"); + return t.time * 1000L + t.millitm; +} + +BlockingLinesSource::TimeoutInfo *BlockingLinesSource::getNextTimeoutInfo () +{ + TimeoutInfo *nextTimeout = NULL; + + for (Iterator<TimeoutInfo> it = timeoutInfos->iterator (); + it.hasNext (); ) { + TimeoutInfo *timeout = it.getNext(); + if (nextTimeout == NULL || + timeout->getTime () < nextTimeout->getTime ()) + nextTimeout = timeout; + } + + return nextTimeout; +} + +void BlockingLinesSource::processTimeouts () +{ + long currentTime = getCurrentTime (); + + while (true) { + TimeoutInfo *nextTimeout = getNextTimeoutInfo (); + if (nextTimeout == NULL) + break; + + PRINTF ("processTimeouts: %ld > %ld? %s", + nextTimeout->getTime (), currentTime, + nextTimeout->getTime () > currentTime ? "yes" : "no"); + if (nextTimeout->getTime () > currentTime) + break; + + PRINT ("processTimeouts: call timeout"); + + getSink()->timeout (nextTimeout->getType ()); + timeoutInfos->remove (nextTimeout); + } +} + +} // namespace tools + +} // namespace rtfl diff --git a/common/lines.hh b/common/lines.hh new file mode 100644 index 0000000..d3bfc75 --- /dev/null +++ b/common/lines.hh @@ -0,0 +1,121 @@ +#ifndef __COMMON_LINES_HH__ +#define __COMMON_LINES_HH__ + +#include "lout/object.hh" +#include "lout/container.hh" + +namespace rtfl { + +namespace tools { + +class LinesSource; + +class LinesSink: public lout::object::Object +{ +public: + virtual void setLinesSource (LinesSource *source) = 0; + virtual void processLine (char *line) = 0; + virtual void timeout (int type) = 0; + virtual void finish () = 0; +}; + + +class LinesSource: public lout::object::Object +{ +public: + virtual void setup (LinesSink *sink) = 0; + virtual void addTimeout (double secs, int type) = 0; + virtual void removeTimeout (int type) = 0; +}; + + +class LinesSourceSequence: public LinesSource +{ +private: + class VirtualSink: public LinesSink + { + public: + LinesSourceSequence *sequence; + + VirtualSink (); + void setLinesSource (LinesSource *source); + void processLine (char *line); + void timeout (int type); + void finish (); + }; + + VirtualSink virtualSink; + LinesSink *sink; + lout::container::typed::List<LinesSource> *sources; + bool setupCalled; + lout::container::typed::Iterator<LinesSource> iterator; + +public: + LinesSourceSequence (bool ownerOfSources); + ~LinesSourceSequence (); + void add (LinesSource *source); + void setup (LinesSink *sink); + void addTimeout (double secs, int type); + void removeTimeout (int type); +}; + + +class FileLinesSource: public LinesSource +{ +private: + enum { MAX_LINE_SIZE = 1000 }; + + tools::LinesSink *sink; + char buf[MAX_LINE_SIZE + 1]; + int bufPos; + bool completeLine; + +protected: + FileLinesSource (); + + int processInput (int fd); + inline void setSink (LinesSink *sink) { + this->sink = sink; sink->setLinesSource (this); } + inline LinesSink *getSink () { return sink; } +}; + + +class BlockingLinesSource: public FileLinesSource +{ +private: + class TimeoutInfo: public lout::object::Object + { + private: + long time; + int type; + + public: + TimeoutInfo (long time, int type); + bool equals(Object *other); + int hashValue(); + + inline long getTime () { return time; } + inline int getType () { return type; } + }; + + int fd; + lout::container::typed::HashSet<TimeoutInfo> *timeoutInfos; + + long getCurrentTime (); + TimeoutInfo *getNextTimeoutInfo (); + void processTimeouts (); + +public: + BlockingLinesSource (int fd); + ~BlockingLinesSource (); + void setup (LinesSink *sink); + void addTimeout (double secs, int type); + void removeTimeout (int type); +}; + + +} // namespace tools + +} // namespace rtfl + +#endif // __COMMON_LINES_HH__ diff --git a/common/parser.cc b/common/parser.cc new file mode 100644 index 0000000..3311e45 --- /dev/null +++ b/common/parser.cc @@ -0,0 +1,250 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "parser.hh" + +#include <string.h> +#include <ctype.h> + +namespace rtfl { + +namespace tools { + +void Parser::setLinesSource (LinesSource *source) +{ +} + +void Parser::processLine (char *line) +{ + char *lineCopy = strdup (line); + + if (strncmp (lineCopy, "[rtfl]", 6) == 0) { + // Pre-version: starts with "[rtfl]". + + char **parts = split (lineCopy + 6, 5); + + if (parts[1] && parts[2] && parts[3]) { + // Notice that parts[4] (arguments) is allowed to be NULL here. + CommonLineInfo info = + { parts[0], atoi(parts[1]), atoi(parts[2]), line }; + processCommand (&info, parts[3], parts[4]); + } else + fprintf (stderr, "Incomplete line:\n%s\n", line); + + freeSplit (parts); + } else if (strncmp (lineCopy, "[rtfl-", 6) == 0) { + // Versioned: starts with "[rtfl-<module>-<major>.<minor>]". + + int i = 6; + while (isalpha (lineCopy[i])) + i++; + + if (lineCopy[i] != '-') + fprintf (stderr, "Expected '-' after module:\n%s\n", line); + else { + char *module = new char[i - 6 + 1]; + memcpy (module, lineCopy + 6, (i - 6) * sizeof (char)); + module[i - 6] = 0; + + i++; + if (!isdigit (lineCopy[i])) + fprintf (stderr, "Missing major version:\n%s\n", line); + else { + int majorVersion = 0, minorVersion = 0; + + while (isdigit (lineCopy[i])) { + majorVersion = 10 * majorVersion + (lineCopy[i] - '0'); + i++; + } + + if (majorVersion == 0) + fprintf (stderr, "Major version must be positive:\n%s\n", line); + else if (lineCopy[i] != '.') + fprintf (stderr, "Expected '.' after major version:\n%s\n", + line); + else if (!isdigit (lineCopy[i + 1])) + fprintf (stderr, "Missing minor version:\n%s\n", line); + else { + i++; + while (isdigit (lineCopy[i])) { + minorVersion = 10 * minorVersion + (lineCopy[i] - '0'); + i++; + } + + if (lineCopy[i] != ']') + fprintf (stderr, "Expected ']' after minor version:\n%s\n", + line); + else { + char **parts = splitEscaped (lineCopy + i + 1); + + if (parts[1] && parts[2] && parts[3]) { + // Notice that parts[4] (first argument) is allowed to be + // NULL here. + CommonLineInfo info = { parts[0], atoi(parts[1]), + atoi(parts[2]), line }; + processVCommand (&info, module, majorVersion, minorVersion, + parts[3], parts + 4); + } else + fprintf (stderr, "Incomplete line:\n%s\n", line); + + freeSplitEscaped (parts); + } + } + } + + delete[] module; + } + } + + free (lineCopy); +} + +void Parser::finish () +{ +} + +void Parser::timeout (int type) +{ +} + +char **Parser::splitEscaped (char *txt) +{ + int numParts; + char **parts; + + scanSplit (txt, &numParts, NULL); + parts = new char*[numParts + 1]; + scanSplit (txt, NULL, parts); + parts[numParts] = NULL; + + for (int i = 0; i < numParts; i++) + unquote (parts[i]); + + return parts; +} + +void Parser::scanSplit (char *txt, int *numParts, char **parts) +{ + int iChar, iPart; + bool quoted; + + if (numParts) + *numParts = 1; + + if (parts) + parts[0] = txt; + + for (iChar = 0, iPart = 1; txt[iChar]; iChar++) { + if (txt[iChar] == '\\' && txt[iChar + 1]) { + iChar++; + quoted = true; + } else + quoted = false; + + if (!quoted && txt[iChar] == ':') { + if (parts) { + txt[iChar] = 0; + parts[iPart] = txt + iChar + 1; + } + + iPart++; + + if (numParts) + (*numParts)++; + } + } +} + +void Parser::unquote (char *txt) +{ + int i, j; + for (i = 0, j = 0; txt[i]; i++, j++) { + if (txt[i] == '\\' && txt[i + 1]) + i++; + txt[j] = txt[i]; + } + txt[j] = 0; +} + +// Free result of splitEscaped(). +void Parser::freeSplitEscaped (char **parts) +{ + delete[] parts; +} + +// Split without escaping. +char **Parser::split (char *txt, int maxNum) +{ + // Only maxNum splits. If less parts are found, less parts are + // returned, so the caller should check the result (first part is + // always defined). Notice that the original text buffer (txt) is + // destroyed, for speed. + + //printf ("===> split ('%s', %d)\n", txt, maxNum); + + char **parts = new char*[maxNum + 1]; + + char *start = txt; + int i = 0; + while (i < maxNum) { + char *end = start; + while (*end != 0 && *end != ':') end++; + int endOfTxt = *end == 0; + + //printf (" start '%s'\n", start); + //printf (" end '%s' (%d character(s))\n", + // end, (int)(end - start)); + + parts[i] = start; + + if (i < maxNum -1) + *end = 0; + + //printf ("---> %d: '%s'\n", i, start); + + i++; + if (endOfTxt) + break; + + start = endOfTxt ? end : end + 1; + } + + parts[i] = NULL; + return parts; +} + +// Free result of split(). +void Parser::freeSplit (char **parts) +{ + delete[] parts; +} + +} // namespace tools + +} // namespace rtfl diff --git a/common/parser.hh b/common/parser.hh new file mode 100644 index 0000000..30cf02d --- /dev/null +++ b/common/parser.hh @@ -0,0 +1,47 @@ +#ifndef __COMMON_PARSER_HH__ +#define __COMMON_PARSER_HH__ + +#include "lines.hh" + +namespace rtfl { + +namespace tools { + +struct CommonLineInfo +{ + char *fileName; + int lineNo; + int processId; + char *completeLine; +}; + +class Parser: public LinesSink +{ +private: + char **splitEscaped (char *txt); + void scanSplit (char *txt, int *numParts, char **parts); + static void unquote (char *txt); + void freeSplitEscaped (char **parts); + +protected: + char **split (char *txt, int maxNum); + void freeSplit (char **parts); + + virtual void processCommand (CommonLineInfo *info, char *cmd, char *args) + = 0; + virtual void processVCommand (CommonLineInfo *info, const char *module, + int majorVersion, int minorVersion, + const char *cmd, char **args) = 0; + +public: + void setLinesSource (LinesSource *source); + void processLine (char *line); + void finish (); + void timeout (int type); +}; + +} // namespace common + +} // namespace rtfl + +#endif // __COMMON_PARSER_HH__ diff --git a/common/rtfl_findrepeat.cc b/common/rtfl_findrepeat.cc new file mode 100644 index 0000000..d464db8 --- /dev/null +++ b/common/rtfl_findrepeat.cc @@ -0,0 +1,534 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * This program searches for identical sequences in a stream of RTFL + * messages (actually, any stream) and marks the beginnings and ends + * with an RTFL mark (obj-mark). You should first filter out other + * lines, so run + * + * $ ... | grep '^\[rtfl[^\]]*\]' | rtfl-findrepeat + * + * or use the script rtfl-objfilter. + * + * For options, see printHelp(). + * + * Warning: This program is highly experimental, and especially rather + * inefficient. Some ideas: + * + * - When finding a suitable lenght ("-l find"), a smaller number of + * lines is in many cases sufficient, so use "head". + * + * - Unless the length is searched for ("-l find"), hashing multiple + * lines (as many as are searched as minimum), as done in the + * searching algorithm by Rabin and Karp, may increase the speed. + */ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include "tools.hh" +#include "../lout/object.hh" +#include "../lout/container.hh" + +using namespace lout::misc; +using namespace lout::object; +using namespace lout::container::typed; + +enum { MAX_LINE_SIZE = 1000 }; + +class Region: public Comparable +{ + int first, num; + +public: + inline Region (int first, int num) { this->first = first; this->num = num; } + + bool equals (Object *other); + int hashValue (); + void intoStringBuffer (StringBuffer *sb); + int compareTo(Comparable *other); + + inline int getFirst () { return first; } + inline int getNum () { return num; } + inline Region *cloneRegion () { return new Region (first, num); } + + inline bool subSetOf (Region *other) + { return first >= other->first && first + num <= other->first + other->num; } +}; + +class Mark: public Object +{ +public: + enum Type { START, END }; + +private: + Type type; + int majorNo, minorNo, length; + +public: + inline Mark (Type type, int majorNo, int minorNo, int length) + { this->type = type; this->majorNo = majorNo; this->minorNo = minorNo; + this->length = length; } + + void intoStringBuffer (StringBuffer *sb); + + inline Type getType () { return type; } + inline int getMajorNo () { return majorNo; } + inline int getMinorNo () { return minorNo; } + inline int getLength () { return length; } +}; + +bool Region::equals (Object *other) +{ + Region *otherRegion = (Region*)other; + return first == otherRegion->first && num == otherRegion->num; +} + +int Region::hashValue () +{ + return first ^ num; +} + +void Region::intoStringBuffer (StringBuffer *sb) +{ + char buf[32]; + + sb->append ("("); + snprintf (buf, 32, "%d", first); + sb->append (buf); + sb->append ("..."); + snprintf (buf, 32, "%d", first + num - 1); + sb->append (buf); + sb->append (")"); +} + +void Mark::intoStringBuffer (StringBuffer *sb) +{ + char buf[32]; + + + sb->append ("("); + sb->append (type == START ? "START" : "END"); + sb->append (" / "); + snprintf (buf, 32, "%d", majorNo); + sb->append (buf); + sb->append (" / "); + snprintf (buf, 32, "%d", minorNo); + sb->append (buf); + sb->append (")"); +} + +int Region::compareTo(Comparable *other) +{ + Region *otherRegion = (Region*)other; + return first - otherRegion->first; +} + +// ---------------------------------------------------------------------- + +static bool debug = false; + +static void printHelp (const char *argv0) +{ + fprintf + (stderr, "Usage: %s <options>\n" + "\n" + "Options:\n" + " -l <n> Search for sequence of at least <n> lines.\n" + " -c <n> Search for sequence repeated at least <n> times.\n" + "\n" + "If an arguments is 'f' or 'find', the maximal value for this is\n" + "determined (possibly with the other argument set to a concrete\n" + "number).\n" + "\n" + "See RTFL documentation for more details.\n", + argv0); +} + +// ---------------------------------------------------------------------- + +static void readFile (FILE *file, Vector<String> *lines, + HashTable<String, Vector<Integer> > *lineNosByLines) +{ + char buf[MAX_LINE_SIZE + 1]; + + for (int lineNo = 0; fgets (buf, MAX_LINE_SIZE + 1, file); lineNo++) { + size_t l = strlen (buf); + if (buf[l - 1] == '\n') buf[l - 1] = 0; + + String *line = new String (buf); + + Vector<Integer> *lineNos = lineNosByLines->get (line); + if (lineNos == NULL) { + lineNos = new Vector<Integer> (1, true); + // Note: key is dublicated. + lineNosByLines->put (new String (buf), lineNos); + } + lineNos->put (new Integer (lineNo)); + + lines->put (line); + } +} + +static int findRegions (Vector<String> *lines, + HashTable<String, Vector<Integer> > *lineNosByLines, + List <HashSet<Region> > *allSetsOfRegions, + int minLength, int minCount) +{ + int effMinLength = minLength == -1 ? 2 :minLength; + int effMinCount = minCount == -1 ? 2 : minCount; + int maxLength = 0, maxCount = 0; + + HashTable<Region, HashSet<Region> > *setsOfRegionsByRegion = + new HashTable<Region, HashSet<Region> > (false, false); + + List <HashSet<Region> > *tmpAllSetsOfRegions = + new List<HashSet<Region> > (false); + + for (int lineNo1 = 0; lineNo1 < lines->size (); lineNo1++) { + String *line = lines->get (lineNo1); + Vector<Integer> *lineNos = lineNosByLines->get (line); + + // Examine only lines after this. + Integer lineNo1Key (lineNo1); + + for (int linesNoIndex = lineNos->bsearch (&lineNo1Key, true) + 1; + linesNoIndex < lineNos->size (); linesNoIndex++) { + int lineNo2 = lineNos->get(linesNoIndex)->getValue (); + int numMatching = 1; + while (lineNo2 + numMatching < lines->size () && + lines->get(lineNo1 + numMatching)->equals + (lines->get(lineNo2 + numMatching))) { + numMatching++; + + if (numMatching >= effMinLength) { + //printf ("equal: (%d...%d) and (%d...%d)\n", + // lineNo1, lineNo1 + numMatching - 1, + // lineNo2, lineNo2 + numMatching - 1); + + Region r1 (lineNo1, numMatching), r2 (lineNo2, numMatching); + HashSet<Region> *setOfRegions; + + if ((setOfRegions = setsOfRegionsByRegion->get (&r1))) { + if (!setsOfRegionsByRegion->contains (&r2)) { + assert (!setOfRegions->contains (&r2)); + Region *rr2 = r2.cloneRegion (); + setOfRegions->put (rr2); + setsOfRegionsByRegion->put (rr2, setOfRegions); + } + } else if ((setOfRegions = setsOfRegionsByRegion->get (&r2))) { + if (!setsOfRegionsByRegion->contains (&r1)) { + assert (!setOfRegions->contains (&r1)); + Region *rr1 = r1.cloneRegion (); + setOfRegions->put (rr1); + setsOfRegionsByRegion->put (rr1, setOfRegions); + } + } else { + Region *rr1 = r1.cloneRegion (), *rr2 = r2.cloneRegion (); + setOfRegions = new HashSet<Region> (false); + setOfRegions->put (rr1); + setOfRegions->put (rr2); + setsOfRegionsByRegion->put (rr1, setOfRegions); + setsOfRegionsByRegion->put (rr2, setOfRegions); + tmpAllSetsOfRegions->append (setOfRegions); + } + + if (debug) { + StringBuffer sb; + setsOfRegionsByRegion->intoStringBuffer (&sb); + printf ("findRegions: setsOfRegionsByRegion = %s\n", + sb.getChars ()); + } + } + } + } + } + + delete setsOfRegionsByRegion; + + for (Iterator<HashSet<Region> > it1 = tmpAllSetsOfRegions->iterator (); + it1.hasNext (); ) { + HashSet<Region> *set = it1.getNext (); + if (set->size () >= effMinCount) { + allSetsOfRegions->append (set); + maxCount = max (maxCount, set->size ()); + + if (minLength == -1) { + for (Iterator<Region> it2 = set->iterator (); it2.hasNext (); ) { + Region *r = it2.getNext (); + maxLength = max (maxLength, r->getNum ()); + } + } + } else + delete set; + } + + delete tmpAllSetsOfRegions; + + if (minLength == -1) + return maxLength; + else if (minCount == -1) + return maxCount; + else + return -1; +} + +static void sortListsOfRegions (List <HashSet<Region> > *allSetsOfRegions, + List <Vector<Region> > *allListsOfRegions) +{ + for (Iterator<HashSet<Region> > it1 = allSetsOfRegions->iterator (); + it1.hasNext (); ) { + HashSet<Region> *set = it1.getNext (); + Vector<Region> *list = new Vector<Region> (1, true); + + for (Iterator<Region> it2 = set->iterator (); it2.hasNext (); ) { + Region *r = it2.getNext (); + list->put (r); + } + + if (debug) { + StringBuffer sb; + list->intoStringBuffer (&sb); + printf ("sortListsOfRegions: list = %s\n", sb.getChars ()); + } + + list->sort (); + allListsOfRegions->append (list); + } +} + +static void cleanupRegions (List <Vector <Region> > *allListsOfRegions) +{ + HashTable<List<Integer>, Vector<Vector<Region> > > *allListsOfLists = + new HashTable<List<Integer>, Vector<Vector<Region> > > (true, true); + + for (Iterator<Vector <Region> > it = allListsOfRegions->iterator (); + it.hasNext (); ) { + Vector<Region> *list = it.getNext (); + + List<Integer> *key = new List<Integer> (true); + for (int i = 1; i < list->size (); i++) + key->append (new Integer (list->get(i)->getFirst () - + list->get(i - 1)->getFirst ())); + + Vector<Vector<Region> > *listOfLists = allListsOfLists->get (key); + if (listOfLists) + delete key; + else { + listOfLists = new Vector<Vector<Region> > (1, false); + allListsOfLists->put (key, listOfLists); + } + + listOfLists->put (list); + } + + allListsOfRegions->clear (); + + if (debug) { + StringBuffer sb; + allListsOfLists->intoStringBuffer (&sb); + printf ("cleanupRegions: allListsOfLists = %s\n", sb.getChars ()); + } + + for (Iterator<List<Integer> > it = allListsOfLists->iterator (); + it.hasNext (); ) { + List<Integer> *key = it.getNext (); + Vector<Vector<Region> > *listOfLists = allListsOfLists->get (key); + + if (debug) { + StringBuffer sb; + listOfLists->intoStringBuffer (&sb); + printf ("cleanupRegions: listOfLists = %s\n", sb.getChars ()); + } + + for (int i = 0; i < listOfLists->size (); i++) { + Vector<Region> *list1 = listOfLists->get (i); + Region *r1 = list1->get (0); + bool redundant = false; + for (int j = 0; j < listOfLists->size () && !redundant; j++) { + if (i != j) { + Vector<Region> *list2 = listOfLists->get (j); + if (list2 != NULL) { + Region *r2 = list2->get (0); + if (r1->subSetOf (r2)) + redundant = true; + } + } + } + + if (redundant) { + listOfLists->put (NULL, i); + delete list1; + } else + allListsOfRegions->append (list1); + } + } + + delete allListsOfLists; +} + +// ---------------------------------------------------------------------- + +int main (int argc, char *argv[]) +{ + int minLength = 2, minCount = 2; + int opt; + + while ((opt = getopt(argc, argv, "c:dl:")) != -1) { + switch (opt) { + case 'c': + if (strcmp (optarg, "f") == 0 || strcmp (optarg, "find") == 0) + minCount = -1; + else + minCount = atoi (optarg); + break; + + case 'd': + debug = true; + break; + + case 'l': + if (strcmp (optarg, "f") == 0 || strcmp (optarg, "find") == 0) + minLength = -1; + else + minLength = atoi (optarg); + break; + + default: + printHelp (argv[0]); + return 1; + } + } + + Vector<String> *lines = new Vector<String> (8, true); + HashTable<String, Vector<Integer> > *lineNosByLines = + new HashTable<String, Vector<Integer> > (true, true); + + readFile (stdin, lines, lineNosByLines); + + List <HashSet<Region> > *allSetsOfRegions = + new List<HashSet<Region> > (true); + + int numFound = findRegions (lines, lineNosByLines, allSetsOfRegions, + minLength, minCount); + + if (debug) { + StringBuffer sb; + allSetsOfRegions->intoStringBuffer (&sb); + printf ("main: allSetsOfRegions = %s\n", sb.getChars ()); + } + + delete lineNosByLines; + + if (numFound != -1) { + delete allSetsOfRegions; + printf ("%d\n", numFound); + } else { + List <Vector<Region> > *allListsOfRegions = + new List <Vector<Region> > (false); // TODO Memory leak! + + sortListsOfRegions (allSetsOfRegions, allListsOfRegions); + + delete allSetsOfRegions; + + if (debug) { + StringBuffer sb; + allListsOfRegions->intoStringBuffer (&sb); + printf ("(a) main: allListsOfRegions = %s\n", sb.getChars ()); + } + + cleanupRegions (allListsOfRegions); + + if (debug) { + StringBuffer sb; + allListsOfRegions->intoStringBuffer (&sb); + printf ("(b) main: allListsOfRegions = %s\n", sb.getChars ()); + } + + HashTable<Integer, List<Mark> > *marksByLineNo = + new HashTable<Integer, List<Mark> > (true, true); + + int majorNo = 0; + for (Iterator<Vector<Region> > it1 = allListsOfRegions->iterator (); + it1.hasNext (); ) { + Vector<Region> *list = it1.getNext (); + int minorNo = 0; + + for (Iterator<Region> it2 = list->iterator (); it2.hasNext (); ) { + Region *r = it2.getNext (); + + for (int typeNo = 0; typeNo < 2; typeNo++) { + Mark::Type type = typeNo == 0 ? Mark::START : Mark::END; + int lineNo = + r->getFirst () + (type == Mark::START ? 0 : r->getNum ()); + Integer lineNoKey (lineNo); + + List<Mark> *list = marksByLineNo->get (&lineNoKey); + if (list == NULL) { + list = new List<Mark> (true); + marksByLineNo->put (new Integer (lineNo), list); + } + + list->append (new Mark (type, majorNo, minorNo, r->getNum ())); + } + + + minorNo++; + } + + majorNo++; + } + + delete allListsOfRegions; + + if (debug) { + StringBuffer sb; + marksByLineNo->intoStringBuffer (&sb); + printf ("main: marksByLineNo = %s\n", sb.getChars ()); + } + + for (int lineNo = 0; lineNo < lines->size (); lineNo++) { + Integer lineNoKey (lineNo); + List<Mark> *list = marksByLineNo->get (&lineNoKey); + if (list) { + for (Iterator<Mark> it = list->iterator (); it.hasNext (); ) { + Mark *m = it.getNext (); + char buf[200]; + rtfl::tools::numToRoman (m->getMajorNo () + 1, buf, + sizeof (buf)); + // Certainly no ':' or '\' in the message, so no quoting + // necessary. + printf ("[rtfl-obj-1.0]n:0:0:mark:findrepeat:findrepeat:0:" + "Sequence %s (length %d), %d%s occurence -- %s\n", + buf, m->getLength (), m->getMinorNo () + 1, + rtfl::tools::numSuffix (m->getMinorNo () + 1), + m->getType () == Mark::START ? "start" : "end"); + } + } + + String *line = lines->get (lineNo); + puts (line->chars ()); + } + + delete marksByLineNo; + } + + delete lines; +} diff --git a/common/rtfl_tee.c b/common/rtfl_tee.c new file mode 100644 index 0000000..4911b73 --- /dev/null +++ b/common/rtfl_tee.c @@ -0,0 +1,249 @@ +/* + * RTFL + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Like tee(1), this program duplicates a stream; however, it does not + * write the copy to a file, but instead sends it via pipe to another + * program. Example: + * + * $ foo | rtfl-tee bar | qix + * + * Here, the standard output of "foo" is passed to the standard input + * of both "bar" and "qix". + * + * More informations in doc/rtfl.html. + * + * TODO: Something like "echo -n foo | rtfl-tee -b cat" does not work; + * since the line of the first "foo" is never finished, the copy of + * "foo" is never printed. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +static void usrerr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + exit (1); +} + +static void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +static ssize_t ewrite(int fd, const void *buf, size_t count) +{ + ssize_t w; + if ((w = write (fd, buf, count)) == -1) + syserr ("write(%d, ...) failed", fd); + return w; +} + +static void writestdout (int orig, char *buf, size_t count) +{ + // Basic idea: "orig" denotes to 0 (stdin of rtfl-tee) or 1 (stdout + // of the called program). "curorig" refers to the origin which is + // currently printed, so than the data from the other origin must + // be buffered. "startline" is set to 1 at the beginning, or iff + // the last printed character was '\n'. (In this case, switching is + // simply possible.) + + static int curorig = 0, startline = 1; + static char obuf[2048]; + static size_t ocount = 0; + + //printf ("\nwritestdout: %d, '%c...' (%d)\n", + // orig, count > 0 ? buf[0] : '.', (int)count); + //printf ("===> curorig = %d, ocount = %d, startline = %d\n", + // curorig, (int)ocount, startline); + + if (count > 0) { + if (orig != curorig) { + if (startline) { + // Simple switching case. + ewrite (1, obuf, ocount); + ocount = 0; + curorig = orig; + ewrite (1, buf, count); + startline = buf[count - 1] == '\n'; + } else { + // Buffer. + size_t odiff = min (count, ocount - sizeof (obuf)); + memcpy (obuf + ocount, buf, odiff); + ocount += odiff; + } + } else { + if (ocount == 0) { + // Nothing buffered: simply print all data. + ewrite (1, buf, count); + startline = buf[count - 1] == '\n'; + } else { + // Only print everything until the last newline character. + // (Note: printing everything until the *first* newline + // character whould make a larger buffer necessary, but, + // on the other hand, preserve better the original + // (temporal) order of the lines.) + ssize_t i, nl; + for (nl = -1, i = count - 1; nl == -1 && i >= 0; i--) + if (buf[i] == '\n') nl = i; + + if (nl == -1) { + // No newline: no switch. + ewrite (1, buf, count); + startline = 0; + } else { + // Newline: switch. + ewrite (1, buf, nl + 1); + ewrite (1, obuf, ocount); + startline = obuf[ocount - 1] == '\n'; + ocount = min (sizeof (obuf), count - (nl + 1)); + memcpy (obuf, buf + nl + 1, ocount); + curorig = 1 - curorig; + } + } + } + } +} + +int main (int argc, char *argv[]) +{ + int parent2child[2], child2parent[2], i, offsetcmd, bypass = 0, erroropt = 0; + char *argv2[argc - 1 + 1]; + int done1, done2; + + for (offsetcmd = 1; offsetcmd < argc && argv[offsetcmd][0] == '-'; + offsetcmd++) { + if (argv[offsetcmd][1] == 'b') + bypass = 1; + else if (argv[offsetcmd][1] == '-') { + offsetcmd++; + break; + } else + erroropt = 1; + } + + if (erroropt || offsetcmd >= argc) + usrerr ("Usage: %s [-b] [--] <program> [<program options>]", argv[0]); + + if (pipe (parent2child) == -1) syserr ("pipe failed"); + if (bypass && pipe (child2parent) == -1) syserr ("pipe failed"); + + switch (fork ()) { + case -1: + syserr ("fork failed"); + break; + + case 0: + if (close (parent2child[1]) == -1) + syserr ("close(%d) failed", parent2child[0]); + if (close (0) == -1) syserr ("close(0) failed"); + if (dup2 (parent2child[0], 0) == -1) + syserr ("dup2(%d, 0) failed", parent2child[0]); + if (close (parent2child[0]) == -1) + syserr ("close(%d) failed", parent2child[0]); + + if (bypass) { + if (close (child2parent[0]) == -1) + syserr ("close(%d) failed", child2parent[1]); + if (close (1) == -1) syserr ("close(1) failed"); + if (dup2 (child2parent[1], 1) == -1) + syserr ("dup2(%d, 1) failed", child2parent[1]); + if (close (child2parent[1]) == -1) + syserr ("close(%d) failed", child2parent[1]); + } + + for (i = 0; i < argc - offsetcmd; i++) argv2[i] = argv[i + offsetcmd]; + argv2[argc - offsetcmd] = NULL; + if (execvp (argv2[0], argv2) == -1) + syserr ("execvp(\"%s\", ...) failed", argv2[0]); + break; + + default: + if (close (parent2child[0]) == -1) + syserr ("close(%d) failed", parent2child[0]); + if (bypass && close (child2parent[1]) == -1) + syserr ("close(%d) failed", child2parent[1]); + + done1 = 0; + done2 = !bypass; + while (!done1 || !done2) { + //printf ("==> done1 = %d, done2 = %d\n", done1, done2); + + fd_set set; + FD_ZERO (&set); + + if (!done1) FD_SET (0, &set); + if (!done2) FD_SET (child2parent[0], &set); + + int s = select ((!done2 ? child2parent[0] : 0) + 1, + &set, NULL, NULL, NULL); + + //printf ("==> s = %d\n", s); + + if (s == -1) syserr ("select failed"); + else if (s > 0) { + char buf[2048]; + ssize_t n; + + if (!done1 && FD_ISSET(0, &set)) { + if ((n = read (0, buf, sizeof (buf))) == -1) + syserr ("read failed"); + else if (n == 0) { + if (close (parent2child[1]) == -1) + syserr ("close(%d) failed", parent2child[1]); + done1 = 1; + } else if (n > 0) { + ewrite (parent2child[1], buf, n); + writestdout (0, buf, n); + } + } + + if (!done2 && FD_ISSET (child2parent[0], &set)) { + if ((n = read (child2parent[0], buf, sizeof (buf))) == -1) + syserr ("read failed"); + else if (n == 0) { + if (close (child2parent[0]) == -1) + syserr ("close(%d) failed", child2parent[0]); + done2 = 1; + } else if (n > 0) + writestdout (1, buf, n); + } + } + } + break; + } + + return 0; +} diff --git a/common/tools.cc b/common/tools.cc new file mode 100644 index 0000000..7dfb412 --- /dev/null +++ b/common/tools.cc @@ -0,0 +1,264 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "tools.hh" + +#include <stdio.h> +#include <errno.h> + +using namespace lout::object; +using namespace lout::container::untyped; + +namespace rtfl { + +namespace tools { + +const char *numSuffix (int n) +{ + if (n % 10 == 1 && n != 11) + return "st"; + else if (n % 10 == 2 && n != 12) + return "nd"; + else if (n % 10 == 3 && n != 13) + return "rd"; + else + return "th"; +} + +static const char + *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, + *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, + *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, + *const roman_I3[] = { "","M","MM","MMM","MMMM" }; + +void numToRoman (int num, char *buf, int buflen) +{ + int i3, i2, i1, i0; + + if (buflen <= 0) + return; + + i0 = num; + i1 = i0/10; i2 = i1/10; i3 = i2/10; + i0 %= 10; i1 %= 10; i2 %= 10; + if (num < 0 || i3 > 4) /* more than 4999 elements ? */ + snprintf(buf, buflen, "****"); + else + snprintf(buf, buflen, "%s%s%s%s", roman_I3[i3], roman_I2[i2], + roman_I1[i1], roman_I0[i0]); +} + +void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +// ---------------------------------------------------------------------- + +EquivalenceRelation::RefTarget::RefTarget (Object *object, bool ownerOfObject) +{ + this->object = object; + this->ownerOfObject = ownerOfObject; + refCount = 1; + allKeys = new HashSet (false); +} + +EquivalenceRelation::RefTarget::~RefTarget () +{ + if (ownerOfObject) + delete object; + delete allKeys; +} + +// ---------------------------------------------------------------------- + +EquivalenceRelation::RefSource::RefSource (Object *key, RefTarget *target) +{ + this->target = target; + this->key = key; + refTarget (); +} + +EquivalenceRelation::RefSource::~RefSource () +{ + unrefTarget (); +} + +void EquivalenceRelation::RefSource::refTarget () +{ + if (target) { + target->ref (); + target->putKey (key); + } +} + +void EquivalenceRelation::RefSource::unrefTarget () +{ + if (target) { + target->removeKey (key); + target->unref (); + target = NULL; + } +} + +void EquivalenceRelation::RefSource::setTarget (RefTarget *target) +{ + if (target != this->target) { + unrefTarget (); + this->target = target; + refTarget (); + } +} + +// ---------------------------------------------------------------------- + +EquivalenceRelation::EquivalenceRelation (bool ownerOfKeys, bool ownerOfValues) +{ + this->ownerOfKeys = ownerOfKeys; + this->ownerOfValues = ownerOfValues; + sources = new HashTable (ownerOfKeys, true); +} + +EquivalenceRelation::~EquivalenceRelation () +{ + delete sources; +} + +void EquivalenceRelation::put (Object *key, Object *value) +{ + assert (!contains(key)); + + RefTarget *target = new RefTarget (value, ownerOfValues); + RefSource *source = new RefSource (key, target); + target->unref (); + sources->put (key, source); +} + +Object *EquivalenceRelation::get (Object *key) const +{ + RefSource *source = (RefSource*) sources->get(key); + if (source) { + Object *object = source->getTarget()->getObject (); + return object; + } else + return NULL; +} + +bool EquivalenceRelation::contains (Object *key) const +{ + return sources->contains (key); +} + +Iterator EquivalenceRelation::iterator () +{ + return sources->iterator (); +} + +Iterator EquivalenceRelation::relatedIterator (Object *key) +{ + assert (contains (key)); + + RefSource *source = (RefSource*) sources->get (key); + RefTarget *target = source->getTarget (); + return target->getAllKeys()->iterator (); +} + +void EquivalenceRelation::relate (Object *key1, Object *key2) +{ + assert (contains(key1) && contains(key2)); + + RefSource *source1 = (RefSource*) sources->get (key1); + RefSource *source2 = (RefSource*) sources->get (key2); + if (source1->getTarget () != source2->getTarget ()) { + // The first value is kept, the second destroyed. The caller has + // to care about the order. + + // Consider all keys already related to `key2`; this is possible by + // iterating over `RefTarget::allKeys`. To avoid accessing freed memory, + // copy all keys to a new temporary set. + + HashSet target2Keys (false); + for (Iterator it = source2->getTarget()->getAllKeys()->iterator (); + it.hasNext (); ) + target2Keys.put (it.getNext ()); + + for (Iterator it = target2Keys.iterator (); it.hasNext (); ) { + RefSource *otherSource = (RefSource*) sources->get (it.getNext ()); + otherSource->setTarget (source1->getTarget ()); + } + } +} + +void EquivalenceRelation::putRelated (Object *oldKey, Object *newKey) +{ + assert (contains(oldKey) && !contains(newKey)); + + RefSource *oldSource = (RefSource*) sources->get (oldKey); + RefSource *newSource = new RefSource (newKey, oldSource->getTarget ()); + sources->put (newKey, newSource); +} + +void EquivalenceRelation::removeSimple (lout::object::Object *key) +{ + // The order is important: a simple "sources->remove (key)" will + // cause an access to freed memory. + + RefSource *source = (RefSource*) sources->get (key); + source->setTarget (NULL); // Will unref() the target. + sources->remove (key); +} + +void EquivalenceRelation::remove (Object *key) +{ + assert (contains (key)); + + RefSource *source = (RefSource*) sources->get (key); + RefTarget *target = source->getTarget (); + target->ref (); + + for (Iterator it = target->getAllKeys()->iterator (); it.hasNext (); ) { + Object *otherKey = it.getNext (); + + // The order is important: see removeSimple(). + RefSource *otherSource = (RefSource*) sources->get (otherKey); + otherSource->setTarget (NULL); // Will unref() the target. + sources->remove (otherKey); + } + + target->unref (); +} + + +} // namespace tools + +} // namespace dw diff --git a/common/tools.hh b/common/tools.hh new file mode 100644 index 0000000..af8e5e9 --- /dev/null +++ b/common/tools.hh @@ -0,0 +1,80 @@ +#ifndef __COMMON_TOOLS_HH__ +#define __COMMON_TOOLS_HH__ + +#include "lout/object.hh" +#include "lout/container.hh" + +namespace rtfl { + +namespace tools { + +const char *numSuffix (int n); +void numToRoman (int num, char *buf, int buflen); +void syserr (const char *fmt, ...); + +class EquivalenceRelation: public lout::object::Object { +private: + class RefTarget: public lout::object::Object { + private: + bool ownerOfObject; + int refCount; + lout::object::Object *object; + lout::container::untyped::HashSet *allKeys; + + public: + RefTarget (lout::object::Object *object, bool ownerOfObject); + ~RefTarget (); + + inline lout::object::Object *getObject () { return object; } + inline void ref () { refCount++; } + inline void unref () { if (--refCount == 0) delete this; } + + inline lout::container::untyped::HashSet *getAllKeys () + { return allKeys; } + inline void putKey (Object *key) { allKeys->put (key); } + inline void removeKey (Object *key) { allKeys->remove (key); } + }; + + + class RefSource: public lout::object::Object { + RefTarget *target; + lout::object::Object *key; + + void refTarget (); + void unrefTarget (); + + public: + RefSource (lout::object::Object *key, RefTarget *target); + ~RefSource (); + + inline RefTarget *getTarget () { return target; } + void setTarget (RefTarget *target); + }; + + bool ownerOfKeys, ownerOfValues; + lout::container::untyped::HashTable *sources; + + lout::container::untyped::HashSet *initSet (lout::object::Object *o); + + public: + EquivalenceRelation (bool ownerOfKeys, bool ownerOfValues); + ~EquivalenceRelation (); + + void put (lout::object::Object *key, lout::object::Object *value); + lout::object::Object *get (lout::object::Object *key) const; + bool contains (lout::object::Object *key) const; + lout::container::untyped::Iterator iterator (); + lout::container::untyped::Iterator relatedIterator (Object *key); + + void relate (lout::object::Object *key1, lout::object::Object *key2); + void putRelated (lout::object::Object *oldKey, lout::object::Object *newKey); + + void removeSimple (lout::object::Object *key); + void remove (lout::object::Object *key); +}; + +} // namespace tools + +} // namespace rtfl + +#endif // __COMMON_TOOLS_HH__ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..86b30b9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,299 @@ +dnl Process this file with aclocal, autoconf and automake. + +AC_INIT([rtfl], [0.1.1]) + +dnl Detect the canonical target build environment +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE +AC_CONFIG_SRCDIR([objects/rtfl_objview.cc]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) + +dnl Options + +JAVA_HOME="" +WARN_GRAPH=no + +AC_ARG_ENABLE(efence, [ --enable-efence Try to compile and run with Electric Fence], + , enable_efence=no) +AC_ARG_ENABLE(gprof, [ --enable-gprof Try to compile and run with profiling enabled], + , enable_gprof=no) +AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Insure++], + , enable_insure=no) +AC_ARG_ENABLE(rtfl, [ --enable-rtfl Build with RTFL messages (for debugging rendering)]) +AC_ARG_ENABLE(graph2, [ --disable-graph2 Use simple Graph widget instead of Graph2]) +AC_ARG_ENABLE(java, [ --disable-java Build RTFL java agent]) +AC_ARG_WITH(java-home,[ --with-java-home=DIR Specify where to find the JDK], JAVA_HOME=$withval) + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_LIBTOOL + +dnl ---------------------------- +dnl Check our char and int types +dnl ---------------------------- +dnl +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(void *) + +AC_TYPE_INT16_T +AC_TYPE_UINT16_T +AC_TYPE_INT32_T +AC_TYPE_UINT32_T + +dnl ----------------------------------------------------------------- +dnl Check for absolute path of working directory. +dnl This is needed for RTFL, to get full the full paths of the source +dnl file names +dnl ----------------------------------------------------------------- +dnl +BASE_CUR_WORKING_DIR=`pwd` + +dnl -------------------------------------- +dnl Check whether to add /usr/local or not +dnl (this is somewhat a religious problem) +dnl -------------------------------------- +dnl +if test "`$CPP -v < /dev/null 2>&1 | grep '/usr/local/include' 2>&1`" = ""; then + CPPFLAGS="$CPPFLAGS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" +fi + +dnl ---------------------- +dnl Test for FLTK 1.3 library +dnl ---------------------- +dnl +dnl For debugging and to be user friendly +AC_MSG_CHECKING([FLTK 1.3]) +fltk_version="`fltk-config --version 2>/dev/null`" +case $fltk_version in + 1.3.*) AC_MSG_RESULT(yes) + LIBFLTK_CXXFLAGS=`fltk-config --cxxflags` + LIBFLTK_LIBS=`fltk-config --ldflags`;; + ?*) AC_MSG_RESULT(no) + AC_MSG_ERROR(FLTK 1.3 required; version found: $fltk_version);; + *) AC_MSG_RESULT(no) + AC_MSG_ERROR(FLTK 1.3 required; fltk-config not found) +esac + +dnl ------------------------- +dnl Test for Graphviz library +dnl ------------------------- +AC_MSG_CHECKING([Graphviz library >= 2.38.0]) +AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include <graphviz/graphviz_version.h> +#include <ctype.h> +#include <stdlib.h> + +static int version_cmp (const char *v1, const char *v2) +{ + const char *s1 = v1, *s2 = v2; + while (*s1 && *s2) { + if (isdigit (*s1) && isdigit (*s2)) { + char buf1[10], buf2[10]; + int n1 = 0, n2 = 0; + + while (isdigit (*s1)) { + if (n1 < 9) buf1[n1++] = *s1; + s1++; + } + + while (isdigit (*s2)) { + if (n2 < 9) buf2[n2++] = *s2; + s2++; + } + + buf1[n1] = buf2[n2] = 0; + int c = atoi (buf1) - atoi (buf2); + if (c != 0) + return c; + } else { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + } + + return *s1 - *s2; +} +]],[[ +if(version_cmp (PACKAGE_VERSION, "2.38.0") >= 0) + return 0; +else + return 1; +]])], +[AC_MSG_RESULT(yes) + HAS_GRAPHVIZ=yes + AM_CONDITIONAL([HAS_GRAPHVIZ], [true]) + GRAPHVIZ_LIBS="-lcgraph -lgvc"], +[AC_MSG_RESULT(no) + HAS_GRAPHVIZ=no + AM_CONDITIONAL([HAS_GRAPHVIZ], [false])] +[AC_MSG_RESULT(no) + HAS_GRAPHVIZ=no + AM_CONDITIONAL([HAS_GRAPHVIZ], [false]) + AC_MSG_WARN([Testing Graphviz library not possible when cross-compiling.])]) + +dnl ------------ +dnl Test for JDK +dnl ------------ +AC_MSG_CHECKING([JDK]) +if test "$enable_java" = no ; then + AC_MSG_RESULT(disabled) + AM_CONDITIONAL([HAS_JAVA], [false]) + JAVA_HOME="" +else + # Follow symbolic links of javac in $PATH. + if test -z "$JAVA_HOME"; then + if which javac >/dev/null; then + javac=$(which javac) + while test -n "$javac"; do + nextjavac=$(readlink $javac) + if test -z "$nextjavac"; then + JAVA_HOME=${javac%/bin/javac} + if test $JAVA_HOME = $javac; then + AC_MSG_RESULT(no) + AC_MSG_WARN([Javac found at $javac, which is strange.]) + AM_CONDITIONAL([HAS_JAVA], [false]) + JAVA_HOME="" + fi + fi + javac=$nextjavac + done + else + AC_MSG_RESULT(no) + AM_CONDITIONAL([HAS_JAVA], [false]) + JAVA_HOME="" + fi + fi +fi + +if test -n "$JAVA_HOME"; then + if test '!' -e $JAVA_HOME/include/jvmti.h; then + AC_MSG_RESULT(no) + AC_MSG_WARN([$JAVA_HOME/include/jvmti.h not found: JDK not usable.]) + AM_CONDITIONAL([HAS_JAVA], [false]) + JAVA_HOME="" + else + AC_MSG_RESULT(yes) + AM_CONDITIONAL([HAS_JAVA], [true]) + # Hopefully only simple directory names ... + JAVA_CFLAGS="-I$JAVA_HOME/include" + if test '!' -e $JAVA_HOME/include/jni_md.h; then + # For some JDKs, "jni_md.h" is only in a OS specific directory. + JAVA_CFLAGS=$JAVA_CFLAGS" -I"`find $JAVA_HOME/include/ -name jni_md.h \ + | head -1 | xargs dirname` + fi + fi +fi + +dnl -------------------- +dnl Command line options +dnl -------------------- +dnl +if test "x$enable_efence" = "xyes" ; then + LIBS="-lefence $LIBS" +fi +if test "x$enable_gprof" = "xyes" ; then + CXXFLAGS="$CXXFLAGS -pg" +fi +if test "x$enable_insure" = "xyes" ; then + CC="insure -Zoi \"compiler $CC\"" + LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0" +fi +if test "x$enable_rtfl" = "xyes" ; then + CXXFLAGS="$CXXFLAGS -DDBG_RTFL" +fi +if test "$enable_graph2" = no ; then + AM_CONDITIONAL([USE_GRAPH2], [false]) +else + if test $HAS_GRAPHVIZ = yes ; then + AM_CONDITIONAL([USE_GRAPH2], [true]) + CXXFLAGS="$CXXFLAGS -DUSE_GRAPH2" + else + AM_CONDITIONAL([USE_GRAPH2], [false]) + AC_MSG_WARN([Graphviz library >= 2.38.0 not found. Graph2 is not enabled.]) + WARN_GRAPH=yes + fi +fi + +dnl ----------------------- +dnl Checks for header files +dnl ----------------------- +dnl +AC_CHECK_HEADERS(fcntl.h unistd.h sys/uio.h) + +dnl -------------------------- +dnl Check for compiler options +dnl -------------------------- +dnl +if eval "test x$GCC = xyes"; then + if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -D_REENTRANT" + fi + if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -D_THREAD_SAFE" + fi + if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -Wall" + fi + if test "`echo $CFLAGS | grep -e '-W ' -e '-W$' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -W" + fi + if test "`echo $CFLAGS | grep '\-Wno-unused-parameter' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -Wno-unused-parameter" + fi + if test "`echo $CFLAGS | grep '\-Waggregate-return' 2> /dev/null`" = ""; then + CFLAGS="$CFLAGS -Waggregate-return" + fi +fi + +dnl ----------- +dnl CXX options +dnl ----------- +dnl +if eval "test x$GCC = xyes"; then + CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions" +fi + +AC_SUBST(BASE_CUR_WORKING_DIR) +AC_SUBST(LIBFLTK_CXXFLAGS) +AC_SUBST(GRAPHVIZ_LIBS) +AC_SUBST(LIBFLTK_LIBS) +AC_SUBST(JAVA_HOME) +AC_SUBST(JAVA_CFLAGS) +AC_SUBST(datadir) + +AC_CONFIG_FILES([ + Makefile + lout/Makefile + common/Makefile + dw/Makefile + dwr/Makefile + objects/Makefile + tests/Makefile + scripts/Makefile + doc/Makefile + java/Makefile +]) + +AC_OUTPUT + +dnl -------------------------- +dnl Some more notable messages +dnl -------------------------- + +if test "$WARN_GRAPH" = yes; then + echo '----------------------------------------------------------------------' + echo 'NOTE: No Graphviz library >= 2.38.0 was found, and you did not' + echo 'explicitly disable the Graph2 widget, which depends on Graphviz. It is' + echo 'recommend to install the Graphviz library and thus enable Graph2 which' + echo 'greatly improves rendering of rtfl-objview. See README for details.' + echo '----------------------------------------------------------------------' +fi diff --git a/debug_rtfl.hh b/debug_rtfl.hh new file mode 100644 index 0000000..a4b5bca --- /dev/null +++ b/debug_rtfl.hh @@ -0,0 +1,461 @@ +// WARNING: This file has been generated. Do not edit! + +/* + * This file is part of RTFL, see <http://home.gna.org/rtfl/> + * for details. + * + * This file (but not RTFL itself) is in the public domain, since it is only a + * simple implementation of a protocol, containing nothing more than trivial + * work. However, it would be nice to keep this notice, along with the URL + * above. + * + * ---------------------------------------------------------------------------- + * + * Defines macros for printing RTFL commands. See documentation for detail + * (online at <http://home.gna.org/rtfl/doc/rtfl.html>). These macros are only + * active, when the pre-processor variable DBG_RTFL is defined. If not, + * alternatives are defined, which have no effect. + * + * This variant assumes that __FILE__ is only the base of the source file name, + * so, to get the full path, CUR_WORKING_DIR has to be defined. See RTFL + * documentation for more details. + */ + +#ifndef __DEBUG_RTFL_HH__ +#define __DEBUG_RTFL_HH__ + +#ifdef DBG_RTFL + +// ======================================= +// Used by all modules +// ======================================= + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/time.h> + +#define DBG_IF_RTFL if(1) + +#define STMT_START do +#define STMT_END while (0) + +// Prints an RTFL message to stdout. "fmt" contains simple format +// characters how to deal with the additional arguments (no "%" +// preceeding, as in printf) or "q" (which additionally +// (double-)quotes quotation marks) or "c" (short for "#%06x" and used +// for colors), or other characters, which are simply printed. No +// quoting: this function cannot be used to print the characters "d", +// "p", "s" and "q" directly. + +inline void rtfl_print (const char *module, const char *version, + const char *file, int line, int processId, + const char *fmt, ...) +{ + // "\n" at the beginning just in case that the previous line is not + // finished yet. + printf ("\n[rtfl-%s-%s]%s:%d:%d:", module, version, file, line, processId); + + va_list args; + va_start (args, fmt); + + for (int i = 0; fmt[i]; i++) { + int n; + void *p; + char *s; + + switch (fmt[i]) { + case 'd': + n = va_arg(args, int); + printf ("%d", n); + break; + + case 'p': + p = va_arg(args, void*); + printf ("%p", p); + break; + + case 's': + s = va_arg (args, char*); + for (int j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + putchar (s[j]); + } + break; + + case 'q': + s = va_arg (args, char*); + for (int j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + else if (s[j] == '\"') + printf ("\\\\"); // a quoted quoting character + putchar (s[j]); + } + break; + + case 'c': + n = va_arg(args, int); + printf ("#%06x", n); + break; + + default: + putchar (fmt[i]); + break; + } + } + + va_end (args); + + putchar ('\n'); + fflush (stdout); +} + +#define RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, CUR_WORKING_DIR "/" __FILE__, __LINE__, \ + getpid (), "s:" fmt, cmd, __VA_ARGS__) + + +// ================================== +// General module +// ================================== + +#define RTFL_GEN_VERSION "1.0" + +#define RTFL_GEN_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("gen", RTFL_GEN_VERSION, cmd, fmt, __VA_ARGS__) + +#define DBG_GEN_TIME() \ + STMT_START { \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + char buf[32]; \ + snprintf (buf, sizeof (buf), "%ld%06ld", tv.tv_sec, tv.tv_usec); \ + RTFL_GEN_PRINT ("time", "s", buf); \ + } STMT_END + + +// ================================== +// Objects module +// ================================== + +#define RTFL_OBJ_VERSION "1.0" + +#define RTFL_OBJ_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#define DBG_OBJ_MSG(aspect, prio, msg) \ + DBG_OBJ_MSG_O (aspect, prio, this, msg) + +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ + RTFL_OBJ_PRINT ("msg", "p:s:d:s", obj, aspect, prio, msg) + +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG (aspect, prio, msg); \ + } STMT_END + +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG_O (aspect, prio, obj, msg); \ + } STMT_END + +#define DBG_OBJ_MARK(aspect, prio, mark) \ + DBG_OBJ_MARK_O (aspect, prio, this, mark) + +#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) \ + RTFL_OBJ_PRINT ("mark", "p:s:d:s", obj, aspect, prio, mark) + +#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK (aspect, prio, mark); \ + } STMT_END + +#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK_O (aspect, prio, obj, mark); \ + } STMT_END + +#define DBG_OBJ_MSG_START() \ + DBG_OBJ_MSG_START_O (this) + +#define DBG_OBJ_MSG_START_O(obj) \ + RTFL_OBJ_PRINT ("msg-start", "p", obj) + +#define DBG_OBJ_MSG_END() \ + DBG_OBJ_MSG_END_O (this) + +#define DBG_OBJ_MSG_END_O(obj) \ + RTFL_OBJ_PRINT ("msg-end", "p", obj) + +#define DBG_OBJ_ENTER0(aspect, prio, funname) \ + DBG_OBJ_ENTER0_O (aspect, prio, this, funname) + +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:", obj, aspect, prio, funname); + +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", this, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", obj, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_LEAVE() \ + DBG_OBJ_LEAVE_O (this) + +#define DBG_OBJ_LEAVE_O(obj) \ + RTFL_OBJ_PRINT ("leave", "p", obj); + +#define DBG_OBJ_LEAVE_VAL(fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", this, vals); \ + } STMT_END + +#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", obj, vals); \ + } STMT_END + +#define DBG_OBJ_LEAVE_VAL0(val) \ + DBG_OBJ_LEAVE_VAL0_O (this, val) + +#define DBG_OBJ_LEAVE_VAL0_O(obj, val) \ + RTFL_OBJ_PRINT ("leave", "p:s:", obj, val) + +#define DBG_OBJ_CREATE(klass) \ + DBG_OBJ_CREATE_O (this, klass) + +#define DBG_OBJ_CREATE_O(obj, klass) \ + RTFL_OBJ_PRINT ("create", "p:s", obj, klass); + +#define DBG_OBJ_DELETE() \ + DBG_OBJ_DELETE_O (this) + +#define DBG_OBJ_DELETE_O(obj) \ + RTFL_OBJ_PRINT ("delete", "p", obj); + +#define DBG_OBJ_BASECLASS(klass) \ + RTFL_OBJ_PRINT ("ident", "p:p", this, (klass*)this); + +#define DBG_OBJ_ASSOC(parent, child) \ + RTFL_OBJ_PRINT ("assoc", "p:p", parent, child); \ + +#define DBG_OBJ_ASSOC_PARENT(parent) \ + DBG_OBJ_ASSOC (parent, this); + +#define DBG_OBJ_ASSOC_CHILD(child) \ + DBG_OBJ_ASSOC (this, child); + +#define DBG_OBJ_SET_NUM(var, val) \ + DBG_OBJ_SET_NUM_O (this, var, val) + +#define DBG_OBJ_SET_NUM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:d", obj, var, val) + +#define DBG_OBJ_SET_SYM(var, val) \ + DBG_OBJ_SET_SYM_O (this, var, val) + +#define DBG_OBJ_SET_SYM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, val) + +#define DBG_OBJ_SET_BOOL(var, val) \ + DBG_OBJ_SET_BOOL_O (this, var, val) + +#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, (val) ? "true" : "false") + +#define DBG_OBJ_SET_STR(var, val) \ + DBG_OBJ_SET_STR_O (this, var, val) + +#define DBG_OBJ_SET_STR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:\"q\"", obj, var, val) + +#define DBG_OBJ_SET_PTR(var, val) \ + DBG_OBJ_SET_PTR_O (this, var, val) + +#define DBG_OBJ_SET_PTR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:p", obj, var, val) + +#define DBG_OBJ_SET_COL(var, val) \ + DBG_OBJ_SET_COL_O (this, var, val) + +#define DBG_OBJ_SET_COL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:c", obj, var, val) + +#define DBG_OBJ_ARRSET_NUM(var, ind, val) \ + DBG_OBJ_ARRSET_NUM_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:d", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_SYM(var, ind, val) \ + DBG_OBJ_ARRSET_SYM_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) \ + DBG_OBJ_ARRSET_BOOL_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, (val) ? "true" : "false") + +#define DBG_OBJ_ARRSET_STR(var, ind, val) \ + DBG_OBJ_ARRSET_STR_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:\"q\"", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_PTR(var, ind, val) \ + DBG_OBJ_ARRSET_PTR_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:p", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_COL(var, ind, val) \ + DBG_OBJ_ARRSET_COL_O (this, var, ind, val) + +#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:c", obj, var, ind, val) + +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_NUM_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:d", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_SYM_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_BOOL_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, \ + (val) ? "true" : "false") + +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_STR_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:\"q\"", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_PTR_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:p", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) \ + DBG_OBJ_ARRATTRSET_COL_O (this, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:c", obj, var, ind, attr, val) + +#define DBG_OBJ_CLASS_COLOR(klass, color) \ + RTFL_OBJ_PRINT ("class-color", "s:s", klass, color) + +#else /* DBG_RTFL */ + +#define STMT_NOP do { } while (0) + +#define DBG_IF_RTFL if(0) + +#define DBG_GEN_TIME() STMT_NOP +#define DBG_OBJ_MSG(aspect, prio, msg) STMT_NOP +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) STMT_NOP +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) STMT_NOP +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) STMT_NOP +#define DBG_OBJ_MARK(aspect, prio, mark) STMT_NOP +#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) STMT_NOP +#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) STMT_NOP +#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) STMT_NOP +#define DBG_OBJ_MSG_START() STMT_NOP +#define DBG_OBJ_MSG_START_O(obj) STMT_NOP +#define DBG_OBJ_MSG_END() STMT_NOP +#define DBG_OBJ_MSG_END_O(obj) STMT_NOP +#define DBG_OBJ_ENTER0(aspect, prio, funname) STMT_NOP +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) STMT_NOP +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) STMT_NOP +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) STMT_NOP +#define DBG_OBJ_LEAVE() STMT_NOP +#define DBG_OBJ_LEAVE_O(obj) STMT_NOP +#define DBG_OBJ_LEAVE_VAL(fmt, ...) STMT_NOP +#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) STMT_NOP +#define DBG_OBJ_LEAVE_VAL0(val) STMT_NOP +#define DBG_OBJ_LEAVE_VAL0_O(obj, val) STMT_NOP +#define DBG_OBJ_CREATE(klass) STMT_NOP +#define DBG_OBJ_CREATE_O(obj, klass) STMT_NOP +#define DBG_OBJ_DELETE() STMT_NOP +#define DBG_OBJ_DELETE_O(obj) STMT_NOP +#define DBG_OBJ_BASECLASS(klass) STMT_NOP +#define DBG_OBJ_ASSOC(parent, child) STMT_NOP +#define DBG_OBJ_ASSOC_PARENT(parent) STMT_NOP +#define DBG_OBJ_ASSOC_CHILD(child) STMT_NOP +#define DBG_OBJ_SET_NUM(var, val) STMT_NOP +#define DBG_OBJ_SET_NUM_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_SYM(var, val) STMT_NOP +#define DBG_OBJ_SET_SYM_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_BOOL(var, val) STMT_NOP +#define DBG_OBJ_SET_BOOL_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_STR(var, val) STMT_NOP +#define DBG_OBJ_SET_STR_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_PTR(var, val) STMT_NOP +#define DBG_OBJ_SET_PTR_O(obj, var, val) STMT_NOP +#define DBG_OBJ_SET_COL(var, val) STMT_NOP +#define DBG_OBJ_SET_COL_O(obj, var, val) STMT_NOP +#define DBG_OBJ_ARRSET_NUM(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_SYM(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_STR(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_PTR(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_COL(var, ind, val) STMT_NOP +#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) STMT_NOP +#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) STMT_NOP +#define DBG_OBJ_CLASS_COLOR(klass, color) STMT_NOP + +#endif /* DBG_RTFL */ + +#endif /* __DEBUG_RTFL_HH__ */ diff --git a/debug_rtfl.hh.in b/debug_rtfl.hh.in new file mode 100644 index 0000000..9d9fd55 --- /dev/null +++ b/debug_rtfl.hh.in @@ -0,0 +1,310 @@ +/* + * This file is part of RTFL, see <http://home.gna.org/rtfl/> + * for details. + * + * This file (but not RTFL itself) is in the public domain, since it is only a + * simple implementation of a protocol, containing nothing more than trivial + * work. However, it would be nice to keep this notice, along with the URL + * above. + * + * ---------------------------------------------------------------------------- + * + * Defines macros for printing RTFL commands. See documentation for detail + * (online at <http://home.gna.org/rtfl/doc/rtfl.html>). These macros are only + * active, when the pre-processor variable DBG_RTFL is defined. If not, + * alternatives are defined, which have no effect. + * + * This variant assumes that __FILE__ is only the base of the source file name, + * so, to get the full path, CUR_WORKING_DIR has to be defined. See RTFL + * documentation for more details. + */ + +#ifndef __DEBUG_RTFL_HH__ +#define __DEBUG_RTFL_HH__ + +#ifdef DBG_RTFL + +// ======================================= +// Used by all modules +// ======================================= + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/time.h> + +#define DBG_IF_RTFL if(1) + +#define STMT_START do +#define STMT_END while (0) + +// Prints an RTFL message to stdout. "fmt" contains simple format +// characters how to deal with the additional arguments (no "%" +// preceeding, as in printf) or "q" (which additionally +// (double-)quotes quotation marks) or "c" (short for "#%06x" and used +// for colors), or other characters, which are simply printed. No +// quoting: this function cannot be used to print the characters "d", +// "p", "s" and "q" directly. + +inline void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...) +{ + // "\n" at the beginning just in case that the previous line is not + // finished yet. + printf ("\n[rtfl-%s-%s]%s:%d:%d:", module, version, file, line, getpid ()); + + va_list args; + va_start (args, fmt); + + for (int i = 0; fmt[i]; i++) { + int n; + void *p; + char *s; + + switch (fmt[i]) { + case 'd': + n = va_arg(args, int); + printf ("%d", n); + break; + + case 'p': + p = va_arg(args, void*); + printf ("%p", p); + break; + + case 's': + s = va_arg (args, char*); + for (int j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + putchar (s[j]); + } + break; + + case 'q': + s = va_arg (args, char*); + for (int j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + else if (s[j] == '\"') + printf ("\\\\"); // a quoted quoting character + putchar (s[j]); + } + break; + + case 'c': + n = va_arg(args, int); + printf ("#%06x", n); + break; + + default: + putchar (fmt[i]); + break; + } + } + + va_end (args); + + putchar ('\n'); + fflush (stdout); +} + +#define RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, CUR_WORKING_DIR "/" __FILE__, __LINE__, \ + "s:" fmt, cmd, __VA_ARGS__) + + +// ================================== +// General module +// ================================== + +#define RTFL_GEN_VERSION "1.0" + +#define RTFL_GEN_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("gen", RTFL_GEN_VERSION, cmd, fmt, __VA_ARGS__) + +#define DBG_GEN_TIME() \ + STMT_START { \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + char buf[32]; \ + snprintf (buf, sizeof (buf), "%ld%06ld", tv.tv_sec, tv.tv_usec); \ + RTFL_GEN_PRINT ("time", "s", buf); \ + } STMT_END + + +// ================================== +// Objects module +// ================================== + +#define RTFL_OBJ_VERSION "1.0" + +#define RTFL_OBJ_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ + RTFL_OBJ_PRINT ("msg", "p:s:d:s", obj, aspect, prio, msg) + +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG (aspect, prio, msg); \ + } STMT_END + +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char msg[256]; \ + snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \ + DBG_OBJ_MSG_O (aspect, prio, obj, msg); \ + } STMT_END + +#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) \ + RTFL_OBJ_PRINT ("mark", "p:s:d:s", obj, aspect, prio, mark) + +#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK (aspect, prio, mark); \ + } STMT_END + +#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) \ + STMT_START { \ + char mark[256]; \ + snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \ + DBG_OBJ_MARK_O (aspect, prio, obj, mark); \ + } STMT_END + +#define DBG_OBJ_MSG_START_O(obj) \ + RTFL_OBJ_PRINT ("msg-start", "p", obj) + +#define DBG_OBJ_MSG_END_O(obj) \ + RTFL_OBJ_PRINT ("msg-end", "p", obj) + +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:", obj, aspect, prio, funname); + +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", this, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ + STMT_START { \ + char args[256]; \ + snprintf (args, sizeof (args), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", obj, aspect, prio, funname, \ + args); \ + } STMT_END + +#define DBG_OBJ_LEAVE_O(obj) \ + RTFL_OBJ_PRINT ("leave", "p", obj); + +#define DBG_OBJ_LEAVE_VAL(fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", this, vals); \ + } STMT_END + +#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) \ + STMT_START { \ + char vals[256]; \ + snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \ + RTFL_OBJ_PRINT ("leave", "p:s", obj, vals); \ + } STMT_END + +#define DBG_OBJ_LEAVE_VAL0_O(obj, val) \ + RTFL_OBJ_PRINT ("leave", "p:s:", obj, val) + +#define DBG_OBJ_CREATE_O(obj, klass) \ + RTFL_OBJ_PRINT ("create", "p:s", obj, klass); + +#define DBG_OBJ_DELETE_O(obj) \ + RTFL_OBJ_PRINT ("delete", "p", obj); + +#define DBG_OBJ_BASECLASS(klass) \ + RTFL_OBJ_PRINT ("ident", "p:p", this, (klass*)this); + +#define DBG_OBJ_ASSOC(parent, child) \ + RTFL_OBJ_PRINT ("assoc", "p:p", parent, child); \ + +#define DBG_OBJ_ASSOC_PARENT(parent) \ + DBG_OBJ_ASSOC (parent, this); + +#define DBG_OBJ_ASSOC_CHILD(child) \ + DBG_OBJ_ASSOC (this, child); + +#define DBG_OBJ_SET_NUM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:d", obj, var, val) + +#define DBG_OBJ_SET_SYM_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, val) + +#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, (val) ? "true" : "false") + +#define DBG_OBJ_SET_STR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:\"q\"", obj, var, val) + +#define DBG_OBJ_SET_PTR_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:p", obj, var, val) + +#define DBG_OBJ_SET_COL_O(obj, var, val) \ + RTFL_OBJ_PRINT ("set", "p:s:c", obj, var, val) + +#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:d", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, (val) ? "true" : "false") + +#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:\"q\"", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:p", obj, var, ind, val) + +#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d:c", obj, var, ind, val) + +#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:d", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, \ + (val) ? "true" : "false") + +#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:\"q\"", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:p", obj, var, ind, attr, val) + +#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) \ + RTFL_OBJ_PRINT ("set", "p:s.d.s:c", obj, var, ind, attr, val) + +#define DBG_OBJ_CLASS_COLOR(klass, color) \ + RTFL_OBJ_PRINT ("class-color", "s:s", klass, color) + +#else /* DBG_RTFL */ + +#define STMT_NOP do { } while (0) + +#define DBG_IF_RTFL if(0) + +//@inactive + +#endif /* DBG_RTFL */ + +#endif /* __DEBUG_RTFL_HH__ */ diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..0dda147 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST = \ + rtfl.html \ + tipsandtricks.html \ + object-box-01.png diff --git a/doc/object-box-01.png b/doc/object-box-01.png Binary files differnew file mode 100644 index 0000000..7fcc651 --- /dev/null +++ b/doc/object-box-01.png diff --git a/doc/rtfl.html b/doc/rtfl.html new file mode 100644 index 0000000..87bc50e --- /dev/null +++ b/doc/rtfl.html @@ -0,0 +1,997 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en"> + <head> + <title>RTFL documentation</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <style type="text/css"> + div.image, div.contents { + float: right; + margin: 0.5em 0 0.5em 1em + } + + div.image, table.data { + text-align: center + } + + div.contents { + width: 40% + } + + p.imagedesc { + font-size: 0.8em; + font-style: italic + } + + table.data { + border-collapse: collapse; + } + + table.data td, table.data th { + padding: 0.1em 1em; + border: 1px solid; + } + </style> + </head> + <body> + <h1>RTFL Documentation</h1> + + <div class="contents"> + <h2>Contents</h2> + + <ul> + <li><a href="#overview">Overview</a></li> + <li><a href="#synopsis">Synopsis</a></li> + <li><a href="#preparing_the_tested_program">Preparing the tested + program</a> + <ul> + <li><a href="#preparing_autoconfmake">Using RTFL with + <tt>autoconf</tt> and <tt>automake</tt></a></li> + </ul> + </li> + <li><a href="#protocol_and_macros">Protocol and macros</a> + <ul> + <li><a href="#protocol_general_module">General module</a></li> + <li><a href="#protocol_objects_module">Objects module</a></li> + </ul> + </li> + <li><a href="#using_rtfl_objcount">Using <tt>rtfl-objcount</tt></a></li> + <li><a href="#using_rtfl_objview">Using <tt>rtfl-objview</tt></a> + <ul> + <li><a href="#rtfl_objview_basic_usage">Basic usage</a></li> + <li><a href="#rtfl_objview_command_line_options">Command line + options</a></li> + <li><a href="#rtfl_objview_navigation">Navigation</a></li> + <li><a href="#rtfl_objview_hiding_commands">Hiding commands</a></li> + <li><a href="#rtfl_objview_filtering_by_type">Filtering by + type</a></li> + <li><a href="#rtfl_objview_stack_traces">Stack traces</a></li> + <li><a href="#rtfl_objview_marks">Marks</a></li> + <li><a href="#rtfl_objview_filtering_messages">Filtering + messages</a></li> + </ul> + </li> + <li><a href="#using_rtfl_objbase">Using <tt>rtfl-objbase</tt></a></li> + <li><a href="#using_rtfl_tee">Using <tt>rtfl-tee</tt></a></li> + <li><a href="#scripts">Scripts</a></li> + <li><a href="#see_also">See also</a></li> + </ul> + </div> + + <h2 id="overview">Overview</h2> + + <p>RTFL, which stands for <i>Read The Figurative Logfile</i>, is a + both a protocol for structured debug messages, as well as a + collection of programs (currently two, <tt>rtfl-objcount</tt> + and <tt>rtfl-objview</tt>) displaying these debug messages in a + semi-graphical way.</p> + + <p>Programs are prepared to print these special debug messages to + standard output (typically controllable via <tt>#ifdef</tt>); + for C++, there is already a header file which provides + convenient macros. By passing the messages to a viewer program + (<tt>rtfl-objcount</tt>, <tt>rtfl-objview</tt>, or, in the + future, similar programs), it becomes simpler to determine what + the debugged program does.</p> + + <h2 id="synopsis">Synopsis</h2> + + <p>To use RTFL, prepare the program which is to be tested (see + <i><a href="#preparing_the_tested_program">Preparing the tested + program</a></i>), and run</p> + + <p><tt><i>tested-program</i> | rtfl-objcount</tt><br/> + <tt><i>tested-program</i> | rtfl-objview [<i>options</i>]</tt></p> + + <p>or, in the future, other programs, which will become part of + RTFL. The prepared, tested program will print special debug + messages to standard output, which will then be read + by <tt>rtfl-objcount</tt> or <tt>rtfl-objview</tt> via pipe. Of + course, using files instead of pipes is also possible.</p> + + <p><tt>Rtfl-objcount</tt> does not yet support options, the ones + of <tt>rtfl-objview</tt> are described in the section + <i><a href="#rtfl_objview_command_line_options">Command line + options of <tt>rtfl-objview</tt></a></i>.</p> + + <p>Details on using <tt>rtfl-objcount</tt> + and <tt>rtfl-objview</tt> can be found in the + sections <i><a href="#using_rtfl_objcount">Using + <tt>rtfl-objcount</tt></a></i> + and <i><a href="#using_rtfl_objview">Using + <tt>rtfl-objview</tt></a></i>, respectively. Furthermore, + there is a useful filter, <tt>rtfl-tee</tt>, which is described + in <i><a href="#using_rtfl_tee">Using <tt>rtfl-tee</tt></a></i>.</p> + + <p>The filter <tt>rtfl-findrepeat</tt> has been moved + <a href="tipsandtricks.html#attic_rtfl_findrepeat">to the + attic</a>. + + <h2 id="preparing_the_tested_program">Preparing the tested program</h2> + + <p>The program to be tested must print special commands to + standard output. The file <tt>debug_rtfl.hh</tt>, included in + RTFL, provides some convenient macros. Both commands and macros + are described in the section + <i><a href="#protocol_and_macros">Protocol and macros</a></i>. + The macros are active only when the pre-processor variable + <tt>DBG_RTFL</tt> is defined. Furthermore, the full paths of the + source file names should be included into the commands; this + variant prefixes <tt>__FILE__</tt> with <tt>CUR_WORKING_DIR</tt>, + which has to be defined.</p> + + <p>It is best to copy <tt>debug_rtfl.hh</tt> in your project, so + that there is no direct dependencies to RTFL. This file itself + is public domain, so there are no restrictions on <em>using</em> + RTFL.</p> + + <p>See the <tt>tests</tt> directory for some examples. (But notice + that explicitly passing <tt>-DDBG_RTFL</tt>, as in + <tt>tests/Makefile.am</tt> is not “comme il faut”; instead, + provide a way to let the user decide.)</p> + + <h3 id="preparing_autoconfmake">Using RTFL with <tt>autoconf</tt> + and <tt>automake</tt></h3> + + <p>The file <b><tt>configure.ac</tt></b> of RTFL itself shows how + to handle this best by an option <tt>--enable-rtfl</tt>. First, + define this option:</p> + + <pre>AC_ARG_ENABLE(rtfl, [--enable-rtfl Build with RTFL messages])</pre> + + <p>Later, a pre-processor variable has to be defined:</p> + + <pre>if test "x$enable_rtfl" = "xyes" ; then + CXXFLAGS="$CXXFLAGS -DDBG_RTFL" +fi</pre> + + <p>Furthermore, we need <tt>CUR_WORKING_DIR</tt>; this is defined + later; here we define:</p> + + <pre>BASE_CUR_WORKING_DIR=`pwd`</pre> + + <p>and:</p> + + <pre>AC_SUBST(BASE_CUR_WORKING_DIR)</pre> + + <p>Finally, all instances of <b><tt>Makefile.am</tt></b> have to + be prepared. Any file <tt>foo/Makefile.am</tt> should + contain:</p> + + <pre>AM_CPPFLAGS = -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/foo"'</pre> + + <h2 id="protocol_and_macros">Protocol and macros</h2> + + <p>This section describes both the commands which are printed to + standard output, as well as the macros from <tt>debug_rtfl.hh</tt>.</p> + + <p>The whole protocol is divided into modules (currently only + one). Each protocol module has a version number consisting of a + positive major number and a minor number, which may be 0. The + major number changes when the protocol module changes in an + incompatible way, while compatible changes affect the minor + number.<sup><a id="ref-protocol-compat" href="#note-protocol-compat">[1]</a></sup><sup><a id="ref-protocol-version" href="#note-protocol-version">[2]</a></sup></p> + + + <p>Generally, the colon (<tt>:</tt>) is used to divide different + parts of a command. To use the colon in a literal way, not for + division,it can be quoted using the backslash (<tt>\</tt>). + Likewise, a literally used backslash must be quoted + (<tt>\\</tt>).</p> + + <p>In the detailed descriptions below, commands are shortened. All + commands begin with</p> + + <p><tt>[rtfl-<i>module</i>-<i>major version</i>.<i>minor version</i>]<i>file name</i>:<i>line number</i>:<i>process identifier</i>:</tt></p> + + <p>where the prefix <tt>[rtfl-<i>module</i>-<i>major version</i>.<i>minor version</i>]</tt> + is used to filter RTFL messages from other messages printed to + standard output. <I>Major version</i> and <i>minor version</i> + refer to the version of the protocol + module.<sup><a id="ref-mix-versions" href="#note-mix-versions">[3]</a></sup> + Then follows the file name and the line number, where this + message is triggered (this may be used for extensions, like + simple navigation through the source code). The last part is a + process (or thread) identifier, e. g. as returned + by <tt>getpid(2)</tt>. Then follows (not shown here) + the <i>command identifier</i>, which defines basically what to + do.</p> + + <p>It is common to combine protocol module and command identifier, + separated by a hyphen. For example, <tt>obj-create</tt> refers + to the command <tt>create</tt> of the module <tt>obj</tt>. + + <p>The following descriptions only give the part following this common + prefix.<sup><a id="ref-old-protocol" href="#note-old-protocol">[4]</a></sup></p> + + <h3 id="protocol_general_module">General module</h3> + + <p>The general module is identified by <tt>gen</tt>, the current + version is 1.0.</p> + + <p>The general module contains messages intended for use with other + modules.</p> + + <p><b>Command:</b> <tt>time:<i>usecs</i></tt><br/> + <b>Macro:</b> <tt>DBG_GEN_TIME()</tt><br/></p> + + <p>Define the point in time of the <em>previous</em> command, in micro + seconds since a defined, but unspecified, origin. Can be used for simple + profiling.</p> + + <h3 id="protocol_objects_module">Objects module</h3> + + <p>The object module is identified by <tt>obj</tt>, the current + version is 1.0.</p> + + <p>All commands from this module are related to an object, which + is typically the first argument following the command + identifier. For C++, the value of <tt>this</tt>, as printed + by <tt>printf("%p", this)</tt> is most suitable. The first + occurrence of a specific object will "define" the object; a + specific command is not needed (but see + <a href="#protocol_obj_create"><tt>create</tt></a>).</p> + + <p>For most macros dealing with <tt>this</tt>, there is an + additional macro with the suffix <tt>_O</tt>, which expects the + object explicitly. Use these in special cases + when <tt>this</tt> is not suitable. They are not mentioned here; + look into <tt>debug_rtfl.hh</tt> for details.</p> + + <p id="protocol_obj_msg"><b>Command:</b> + <tt>msg:<i>object</i>:<i>aspect</i>:<i>priority</i>:<i>msg</i></tt><br/> + <b>Macros:</b><br/> + <tt>DBG_OBJ_MSG(<i>aspect</i>, <i>priority</i>, <i>msg</i>)</tt><br/> + <tt>DBG_OBJ_MSGF(<i>aspect</i>, <i>priority</i>, <i>fmt</i> ...)</tt></p> + + <p>Display a message (<i>msg</i>) related to an object. The + macro <tt>DBG_OBJ_MSG</tt> expects a simple text, + while <tt>DBG_OBJ_MSGF</tt> can be used to use + <tt>printf(3)</tt> like formatting.</p> + + <p><i>Aspects</i> are arbitrary keywords, which can be used to + focus on different parts of a program (which may affect the same + objects). <i>Priorities</i> are positive numbers defining how + important a message should be regarded, with 0 denoting the most + important message. Both can be filtered by the viewer programs, + see <i><a href="#rtfl_objview_filtering_messages">Filtering + messages in <tt>rtfl-objview</tt></a></i>.</p> + + <p>A very simple variant of HTML can be used in the messages, the + tags <i> and <b> display italics and bold text, + respectively.</p> + + <p id="protocol_obj_mark"><b>Command:</b> + <tt>mark:<i>object</i>:<i>aspect</i>:<i>priority</i>:<i>mark</i></tt><br/> + <tt>DBG_OBJ_MARK(<i>aspect</i>, <i>priority</i>, <i>mark</i>)</tt><br/> + <tt>DBG_OBJ_MARKF(<i>aspect</i>, <i>priority</i>, <i>fmt</i> ...)</tt></p> + + <p>Very similar to + <a href="#protocol_obj_msg"><tt>msg</tt></a>, but + <i>marks</i> are handled in a privileged way; + <a href="#using_rtfl_objview"><tt>rtfl-objview</tt></a> displays + them in a seperate menu, so it is simple to select them in a + fast way.</p> + + <p>Marks are used to mark special positions within the streams of + RTFL commands; although there are macros, they are typically + added by filters, which scan RTFL messages for notable + occurences. An example is + <a href="#scripts"><tt>rtfl-stracktraces</tt></a> with the + option <tt>-m</tt>.</p> + + <p id="protocol_obj_start_end"><b>Commands:</b> + <tt>msg-start:<i>object</i></tt> and + <tt>msg-end:<i>object</i></tt><br/> + <b>Macros:</b> <tt>DBG_OBJ_MSG_START()</tt> and + <tt>DBG_OBJ_MSG_END()</tt></p> + + <p>Define the start and the end of a section, like a + function. Especially useful for recursions, where these commands + are nested.</p> + + <p id="protocol_obj_enter_leave"><b>Commands:</b> + <tt>enter:<i>object</i>:<i>aspect</i>:<i>priority</i>:<i>funname</i>:<i>args</i></tt> + and <tt>leave:<i>object</i></tt> + or <tt>leave:<i>object</i>:<i>vals</i></tt><br/> + <b>Macros:</b><br/> + <tt>DBG_OBJ_ENTER0(<i>aspect</i>, <i>priority</i>, <i>funname</i>)</tt> (for functions/methods with no arguments)<br/> + <tt>DBG_OBJ_ENTER(<i>aspect</i>, <i>priority</i>, <i>funname</i>, <i>fmt</i> ...)</tt><br/> + <tt>DBG_OBJ_LEAVE()</tt><br/> + <tt>DBG_OBJ_LEAVE_VAL(<i>fmt</i> ...)</tt><br/> + <tt>DBG_OBJ_LEAVE_VAL0(<i>val</i>)</tt></p> + + <p>States that a function or a method has been entered or left, + respectively. <i>Aspect</i> and <i>priority</i> have the same + meaning as for <a href="#protocol_obj_msg"><tt>msg</tt></a>. + The <tt>printf(3)</tt> like format <i>fmt</i> (macros + <tt>DBG_OBJ_ENTER</tt> and <tt>DBG_OBJ_LEAVE_VAL</tt>) must + match the following arguments. Any <tt>leave</tt> refers to + the last “open” <tt>enter</tt>. + + <p><tt>Leave</tt> supports optional return values, the + macro <tt>DBG_OBJ_LEAVE_VAL</tt> should be used in this + case. Multiple values are allowed; either with languages which + support this, or indirectly by returning values by passing + references as arguments. For a literal text value, use + <tt>DBG_OBJ_LEAVE_VAL0</tt>.</p> + + <p id="protocol_obj_create"><b>Command:</b> + <tt>create:<i>object</i>:<i>class</i></tt><br/> + <b>Macro:</b> <tt>DBG_OBJ_CREATE(<i>class</i>)</tt></p> + + <p>Assigns a class to an object. This is not really necessary, + but without this command, the class of an object is not known, + and for the user of RTFL, difficult to guess.</p> + + <p>When multiple classes are assigned to one object, the last + class is valid. This way, calling <tt>DBG_OBJ_CREATE()</tt> in + both constructors, the one of the base class (which is called + first) and the one of the sub class (which is called second), + will work properly, by eventually assigning the class of the sub + class.</p> + + <p>For C++, it is recommended to use the fully qualified class + name (<tt>path::to::namespace::ClassName</tt>).</p> + + <p id="protocol_obj_delete"><b>Command:</b> + <tt>delete:<i>object</i></tt><br/> + <b>Macro:</b> <tt>DBG_OBJ_DELETE()</tt></p> + + <p>Deletes an object. Can be called multiple times for one object, + when each destructor the class hierarchy prints this + command.</p> + + <p>After <tt>delete</tt> has been called as often as + <a href="#protocol_obj_create"><tt>create</tt></a> before, the object + identifier must be regarded as unassigned, i. e., if it is used + again, it is regarded as identifying a new object.</p> + + <p id="protocol_obj_ident"><b>Command:</b> + <tt>ident:<i>object1</i>:<i>object2</i></tt><br/> + <b>Macro:</b> <tt>DBG_OBJ_BASECLASS(<i>class</i>)</tt></p> + + <p>This command defines two objects as identical, so that all + commands for <i>object1</i> and <i>object2</i> will be treated + in the same way, in a non-distinguishable way. + + <p>The reasoning behind this command is shown by the macro, and + lies in the way how C++ handles multiple inheritance. Assume a + class <tt>C</tt> which has two super-classes, <tt>A</tt> + and <tt>B</tt>. Typically, when regarding instances + of <tt>C</tt> as <tt>A</tt> (cast to <tt>A*</tt>), the value + of <tt>this</tt> will not change. However, when regarded + as <tt>B</tt>, the value will be different, + typically <tt>sizeof(A)</tt> bytes larger.</p> + + <p>For this reason, <tt>DBG_OBJ_CREATE</tt> should, at least for + cases of multiple inheritance, be followed + by <tt>DBG_OBJ_BASECLASS</tt>: + + <p><tt>DBG_OBJ_CREATE(C);</tt><br/> + <tt>DBG_OBJ_BASECLASS(A);</tt><br/> + <tt>DBG_OBJ_BASECLASS(B);</tt></p> + + <p><b>Command:</b> <tt>noident</tt></p> + + <p>This command tells the processing program that + <a href="#protocol_obj_ident"><tt>obj-ident</tt></a> is never used, which + makes it unnecessary to wait until the (non-)identity of object has been + clarified.</p> + + <p id="protocol_obj_assoc"><b>Command:</b> + <tt>assoc:<i>parent</i>:<i>child</i></tt><br/> + <b>Macros:</b><br/> + <tt>DBG_OBJ_ASSOC(<i>parent</i>, <i>child</i>)</tt><br/> + <tt>DBG_OBJ_ASSOC_PARENT(<i>parent</i>)</tt><br/> + <tt>DBG_OBJ_ASSOC_CHILD(<i>child</i>)</tt></p> + + <p>Creates an association between two objects. Objects have some + kind of hierarchy, not limited to a tree, not even a DAG, but + still not symmetric: <tt>DBG_OBJ_ASSOC(<i>x</i>, <i>y</i>)</tt> + is to be distinguished from <tt>DBG_OBJ_ASSOC(<i>y</i>, <i>x</i>)</tt>. + + <p id="protocol_obj_set"><b>Command:</b> + <tt>set:<i>object</i>:<i>var</i>:<i>val</i></tt><br/> + <b>Macros:</b><br/> + <tt>DBG_OBJ_SET_<i>TYPE</i>(<i>var</i>, <i>val</i>)</tt><br/> + <tt>DBG_OBJ_ARRSET_<i>TYPE</i>(<i>var</i>, <i>ind</i>, <i>val</i>)</tt><br/> + <tt>DBG_OBJ_ARRATTRSET_<i>TYPE</i>(<i>var</i>, <i>ind</i>, <i>attr</i>, <i>val</i>)</tt></p> + + <p>Set or change an attribute of an object. The name can be + divided by using the dot (<tt>.</tt>), both for sub structures + and for arrays. The different macros are used to format + different types:</p> + + <table class="data"> + <thead> + <tr> + <th><i>TYPE</i></th> + <th>C++ type of <i>val</i></th> + <th>Output</th> + </tr> + </thead> + <tbody> + <tr> + <td><tt>NUM</tt></td> + <td><tt>int</tt></td> + <td><i>val</i></td> + </tr> + <tr> + <td><tt>SYM</tt></td> + <td><tt>char*</tt></td> + <td><i>val</i></td> + </tr> + <tr> + <td><tt>BOOL</tt></td> + <td><tt>bool</tt></td> + <td><tt>true</tt> or <tt>false</tt></td> + </tr> + <tr> + <td><tt>STR</tt></td> + <td><tt>char*</tt></td> + <td><tt>"</tt><i>val</i><tt>"</tt></td> + </tr> + <tr> + <td><tt>PTR</tt></td> + <td><tt>void*</tt></td> + <td><i>val</i></td> + </tr> + <tr> + <td><tt>COL</tt></td> + <td><tt>int</tt> (<tt>0x<i>RRGGBB</i></tt>, interpreted as RGB + color)</td> + <td><tt>#<i>RRGGBB</i></tt></td> + </tr> + </tbody> + </table> + + <p>Furthermore, variants use the dot notation for array + elements. The last variant is used for attributes of structures, + which are elements of an array. (For even more complex + notations, you can, of course, define new macros.)</p> + + <p id="protocol_obj_class_color"><b>Command:</b> + <tt>class-color:<i>class</i>:<i>color</i></tt><sup><a id="ref-obj-color-old" href="#note-obj-color-old">[5]</a></sup><br/> + <b>Macro:</b> <tt>DBG_OBJ_CLASS_COLOR(<i>class</i>, + <i>color</i>)</tt><sup><a id="ref-obj-class-color-old" href="#note-obj-class-color-old">[6]</a></sup></p> + + <p>Defines a color for groups of class names. <i>Classes</i> takes + the form of patterns as defined by <tt>fnmatch(3)</tt>, + e. .g. <tt>path::to::namespace::*</tt>; <i>color</i> is + given as <tt>#<i>RGB</i></tt> or <tt>#<i>RRGGBB</i></tt>.</p> + + <p>When more than one pattern matches, the most specific is + applied.<sup><a id="ref-class-color-specifity" href="#note-class-color-specifity">[7]</a></sup><sup><a id="ref-class-color-specifity-old" href="#note-class-color-specifity-old">[8]</a></sup></p> + + <p id="protocol_obj_object_color"><b>Command:</b> + <tt>object-color:<i>object</i>:<i>color</i></tt></p> + + <p>Defines a color for single objects. Like in + <a href="#protocol_obj_class_color">class-color</a>, <i>color</i> + is given as <tt>#<i>RGB</i></tt> or <tt>#<i>RRGGBB</i></tt>.</p> + + <p>Object colors are preferred over + <a href="#protocol_obj_class_color">class colors</a>. + + <h2 id="using_rtfl_objcount">Using <tt>rtfl-objcount</tt></h2> + + <p><tt>Rtfl-objcount</tt> reads RTFL commands from the + <a href="#protocol_objects_module">objects module</a> via + standard input, simply <em>counts</em> the number of instances + of each class, and shows them in a table, which is especially + useful to examine memory problems. Use <i>New snapshot</i> from + the <i>Snapshot</i> menu to preserve a current state: a column + is added, and new changes are only applied to the most right + column. + + <p>Like <a href="#using_rtfl_objbase"><tt>rtfl-objbase</tt></a>, + <tt>rtfl-objcount</tt> first reads commands from a file <tt>.rtfl</tt> in + the current directory, which typically contains commands for the program + to be debugged.</p> + + <h2 id="using_rtfl_objview">Using <tt>rtfl-objview</tt></h2> + + <p><tt>Rtfl-objview</tt> reads RTFL commands from the + <a href="#protocol_objects_module">objects module</a> via + standard input, and displays them as a diagram, with all + commands related to one object as a box, and showing the + relations between the objects.</p> + + <p>Like <a href="#using_rtfl_objbase"><tt>rtfl-objbase</tt></a>, + <tt>rtfl-objview</tt> first reads commands from a file <tt>.rtfl</tt> in + the current directory, which typically contains commands for the program + to be debugged. (See also <a href="#rtfl_objview_option_b">option + <tt>-B</tt></a>.)</p> + + <h3 id="rtfl_objview_basic_usage">Basic usage</h3> + + <div class="image"> + <img src="object-box-01.png"/> + <p class="imagedesc">A typical object box</p> + </div> + + <p>All messages related to an object will be displayed in a box + showing</p> + + <ul> + <li>the object identifier, and when known, the class, at the top;</li> + <li>the attributes in the middle part;</li> + <li>all messages in the lower part.</li> + </ul> + + <p>At different levels, parts of a box can be toggled between + visible and invisible (or, instead, replaced by a much smaller + part, as e. g. used for attributes). This is indicated in + the usual way, by “+” (to show) and “−” (to hide) in the upper + left corner.</p> + + <p>For attributes, the structure based on the dot notation + (see <a href="#protocol_obj_set"><tt>set</tt></a>) creates a + tree whose parts can be toggled between visible and + invisible. The complete history of values is displayed, but can + be hidden by instead only showing the recent value.</p> + + <p>In the messages part, + <a href="#protocol_obj_start_end"><tt>msg-start</tt> and + <tt>msg-end</tt></a> change the indentation of the + messaged between, and also insert the messages <i>start</i> + and <i>end</i>.</p> + + <p>Associations lead to a respective positioning of the object + boxes and connections by arrows, as well as a message in the + messages part.</p> + + <p>(Many aspects can be configured, see + <i><a href="#rtfl_objview_filtering_by_types">Filtering by + types</a></i>.)</p> + + <h3 id="rtfl_objview_command_line_options">Command line options</h3> + + <p><tt>Rtfl-objview</tt> supports these options:</p> + + <dl> + <dt><tt>-a</tt> <i>aspect</i>, <tt>-A</tt> <i>aspect</i></dt> + <dd>Show (<tt>-a</tt>) or hide (<tt>-A</tt>) an aspect + (see <i><a href="#rtfl_objview_filtering_messages">Filtering + messages</a></i>). This is equivalent to turning on/off an + aspect from the <i>Aspects</i> menu. The aspect “*” refers to + all, so this option is then equivalent to <i>Show/Hide all</i> + aspects. Both options can be used multiple times, and are + processed in the order in which they are given; so <tt>-A "*" + -a foo</tt> will only show messages with the aspect + “foo”.</dd> + + <dt id="rtfl_objview_option_b"><tt>-b</tt>, <tt>-B</tt></dt> + <dd>Do (<tt>-b</tt>) or do not (<tt>-B</tt>) apply <tt>.rtfl</tt> and + filtering identities (what + <a href="#using_rtfl_objbase"><tt>rtfl-objbase</tt></a> does).</dd> + + <dt><tt>-m</tt>, <tt>-M</tt></dt> + <dd>Show (<tt>-m</tt>) or hide (<tt>-M</tt>) the messages of all + object boxes. Hiding (<tt>-M</tt>) is useful when examining + attributes; the messages can be shown by clicking on + “+”. (Since showing is the default, the option <tt>-m</tt> + only exists for reasons of symmetry.)</dd> + + <dt><tt>-o</tt>, <tt>-O</tt></dt> + <dd>Show (<tt>-o</tt>) or hide (<tt>-O</tt>) the contents of all + object boxes. Hiding (<tt>-O</tt>) can be useful to get an + overview over the relations, without initially caring about + the contents; the contents can be shown by clicking on + “+”. (Since showing is the default, the option <tt>-o</tt> + only exists for reasons of symmetry.)</dd> + + <dt><tt>-p</tt> <i>priority</i></dt> + <dd>Set the priority of messages (see + <i><a href="#rtfl_objview_filtering_messages">Filtering + messages</a></i>). This is equivalent to choosing the + respective value in the <i>Priorities</i> menu. The + <i>priority</i> “*” refers to <i>No limit</i>.</dd> + + <dt><tt>-t</tt> <i>types</i>, <tt>-T</tt> <i>types</i></dt> + <dd>Show (<tt>-t</tt>) or hide (<tt>-T</tt>) certain command types + (see <i><a href="#rtfl_objview_filtering_by_types">Filtering by + types</a></i>). <i>Types</i> is a sequence of any of these characters: “c” + for creations, “i” for indentations, “m” for messages, “a” for marks, “f” + for functions, “s” for association, “t” for attributes, and “d” for + deletions. This is equivalent to turning on/off the respective types in + the <i>Commands</i> menu.</dd> + + <dt><tt>-v</tt> <i>viewer</i></dt> + <dd>Set the program called for viewing code (see + <i><a href="#rtfl_objview_navigation">Navigation</a></i>). + <i>Viewer</i> is a command (passed to <tt>system(3)</tt>), + which may contain these variables: “%n” is replaced by the + line number, “%p” is replaced by the path of the file. Notice + that you should append “&” to avoid blocking. The default is + “<tt>xterm -e 'vi +%n %p' &</tt>”; another nice option is + “<tt>emacsclient -n %n %p</tt>”. (Also notice that there is + currently no quoting done for expanding “%p”, so it is best to + keep your file names as simple as possible.) + </dl> + + <h3 id="rtfl_objview_navigation">Navigation</h3> + + <p>The commands (from the object module) + <a href="#protocol_obj_start_end"><tt>msg-start</tt>, + <tt>msg-end</tt></a>, + <a href="#protocol_obj_enter_leave"><tt>enter</tt>, + <tt>leave</tt></a>, + <a href="#protocol_obj_msg"><tt>msg</tt></a>, + <a href="#protocol_obj_mark"><tt>mark</tt></a>, + <a href="#protocol_obj_set"><tt>set</tt></a>, + <a href ="#protocol_obj_assoc"><tt>assoc</tt></a>, + <a href="#protocol_obj_create"><tt>create</tt></a>, and + <a href="#protocol_obj_delete"><tt>delete</tt></a> are + <em>navigable</em>. They are assigned a serial numbers (starting + with 0) which is preceding the actual message; furthermore, they + can be selected (by clicking on the respective message) and + navigated with the commands <i>Previous</i> and <i>Next</i> from + the <i>Command</i> menu, or the respective keyboard + accelerators. Furthermore, <i>View Code</i> calls an external + (and <a href="#rtfl_objview_command_line_options">configurable</a>) + viewer or editor to show the line in the code where the + selected message was printed.</p> + + <p>Both preceding the serial number on one hand, and selection and + highlighting on the other, refers to the respective message in + the lower part of the object box (<tt>msg-start</tt>, + <tt>msg-end</tt>, <tt>msg</tt>, <tt>mark</tt>, + <tt>assoc</tt>, <tt>create</tt>, <tt>delete</tt>), or (<tt>set</tt>) + for the message in the attribute value history (click “+” to + show), respectively </p> + + <p>Some navigable commands are <i>related</i>; by + activating <i>Switch between related</i> from the + <i>Commands</i> menu switches between them. Currently + <tt>enter</tt> and <tt>leave</tt> are regarded, as well as + <tt>msg-start</tt> and <tt>msg-end</tt>.</p> + + <h3 id="rtfl_objview_hiding_commands">Hiding commands</h3> + + <p>Displaying of navigable commands can be limited to any + region. Select a command (mouse or navigating) and choose from + the <i>Command</i> menu:</p> + + <ul> + <li><i>Hide before</i> to hide all commands before selected one + (which so becomes the first visible command);</li> + <li><i>Hide after</i> likewise to hide all commands after + selected one (which becomes the last visible command);</li> + <li><i>Hide all</i> to hide all commands but show new commands + (equivalent to hiding before the next command to be + created);</li> + <li><i>Show before</i> to undo <i>Hide before</i>;</li> + <li><i>Show after</i> to undo <i>Hide after</i>;</li> + <li><i>Show all</i> to show all commands again.</li> + </ul> + + <p>Notice that the serial numbers are recalculated, so that the + first <em>visible</em> command has the number 0. This is also + valid for commands filtered out + (<a href="#rtfl_objview_filtering_by_type">by type</a>, or + <a href="#rtfl_objview_filtering_messages">by aspect and + priority</a>).</p> + + <h3 id="rtfl_objview_stack_traces">Stack traces</h3> + + <p>Based on <a href="#protocol_obj_enter_leave"><tt>enter</tt> and + <tt>leave</tt></a>, the command <i>Show stack trace</i> from + the <i>Command</i> menu opens a new window showing + all <tt>enter</tt> commands leading to the selected command. As + long as these are not hidden (which is represented by italics), + you can navigate to these by clicking on <i>Jump to</i>.</p> + + <h3 id="rtfl_objview_marks">Marks</h3> + + <p>All <a href="#protocol_obj_mark"><tt>marks</tt></a> are added to + the <i>Marks</i> menu, from where they can be directly + selected, as long as they are not hidden.</p> + + <h3 id="rtfl_objview_filtering_by_type">Filtering by type</h3> + + <p>All <a href="#rtfl_objview_navigation">navigable commands</a> + can be filtered by types, by using entries in the <i>Command</i> + menu. In all cases, this refers to the navigable part of the + command; other effects (indentation itself, current value of an + attribute, arrows for associations) are not affected.</p> + + <ul> + <li><i>Creations</i> refers to + <a href="#protocol_obj_create"><tt>create</tt></a>,</li> + <li><i>Indentations</i> to + <a href="#protocol_obj_start_end"><tt>msg-start</tt> and + <tt>msg-end</tt></a>,</li> + <li><i>Messages</i> to + <a href="#protocol_obj_msg"><tt>msg</tt></a>,</li> + <li><i>Marks</i> to + <a href="#protocol_obj_mark"><tt>mark</tt></a>,</li> + <li><i>Functions</i> to + <a href="#protocol_obj_enter_leave"><tt>enter</tt> and + <tt>leave</tt></a>,</li> + <li><i>Associations</i> to + <a href="#protocol_obj_assoc"><tt>assoc</tt></a>,</li> + <li><i>Attributes</i> to + <a href="#protocol_obj_set"><tt>set</tt></a>, and</li> + <li><i>Deletions</i> to + <a href="#protocol_obj_delete"><tt>delete</tt></a>.</li> + </ul> + + <p>By default, indentation messages are turned off, all others are + turned on.</p> + + <h3 id="rtfl_objview_filtering_messages">Filtering messages</h3> + + <p><a href="#protocol_obj_msg">Message commands</a> can be + filtered by aspects and priorities.</p> + + <p>All aspects defined by the program are visible in + the <i>Aspects</i> menu, alphabetically sorted, and can be + toggled individually. <i>Show all</i> and <i>Hide all</i> not + only affect all aspects already shown in the <i>Aspects</i> + menu, but also define the default value for newly added aspects + (initially turned on, or initially turned off, + respectively).</p> + + <p>Likewise, the <i>Priorities</i> menu contains all priorities, + numerically sorted. Choosing one defines the <em>maximal</em> + priority number (and so minimal importance); anything above this + number (and so less important) is not shown. <i>No limit</i> + will not filter any messages by priority.</p> + + <p><i>Note:</i> Both aspects and priorities should be sorted + (aspects alphabetically, priorities numerically), which is not + yet the case. Especially for priorities, this can become + confusing. Also, version 0.0.7 contains a bug that <i>No + limit</i> is not selected initially, when it should be. (This is + only a visualization problem, filtering works.)</p> + + <h2 id="using_rtfl_objbase">Using <tt>rtfl-objbase</tt></h2> + + <p><tt>Rtfl-objbase</tt> is a filter which reads RTFL commands from the + <a href="#protocol_objects_module">objects module</a> and writes + them again to standard output, after + + <ul> + <li>reading and applying commands from a file <tt>.rtfl</tt> in the + current directory (which typically contains commands for the program to + be debugged),</li> + <li>dealing with <a href="#protocol_obj_ident"><tt>obj-ident</tt></a>: + identities declared as identical are replaced by actually + identical identites, so a program processing the output may simply + ignore <tt>obj-ident</tt>;</li> + <li>dealing with <a href="#protocol_obj_delete"><tt>obj-delete</tt></a> + and replacing re-used identities of deleted objects by new + identities;</li> + <li>furthermore, the newest version is used for the output.</li> + </ul> + + <p><tt>Rtfl-objbase</tt> is usefully for + <a href="#scripts">scripts</a>.</p> + + <h2 id="using_rtfl_tee">Using <tt>rtfl-tee</tt></h2> + + <p><tt>Rtfl-tee</tt> is a simple program which works similar + to <tt>tee(1)</tt>: it copies standard input to standard output + as well as to a new pipe which is connected to standard input of + another process. The exact syntax is:</p> + + <p><tt>rtfl-tee [-b] [--] <i>command</i> [<i>arguments</i> ...]</tt></p> + + <p>All <i>arguments</i> refer to <i>command</i>.</p> + + <p>The option <tt>-b</tt> enables the <i>bypass</i> mode, in which + the standard output of <i>command</i> is piped to + <tt>rtfl-tee</tt>, which prints both its own standard input and + the standard output of <i>command</i> line by line, so that the + lines from both streams are preserved. Use this + when <i>command</i> is a filter and you want to mix both the + original and the filtered stream in a simple way. Notice that no + order of the lines is guaranteed.</p> + + <p>Everything after <tt>--</tt> is regarded as <i>command</i>. Use + this for commands starting with “-”.</p> + + <p>A typical (simple) example:</p> + + <p><tt><i>tested-program</i> | rtfl-tee rtf-objview -O | rtfl-objcount</tt></p> + + <p>This will start <tt><i>tested-program</i></tt> in a way that + the output is passed both + to <a href="#using_rtfl_objview"><tt>rtfl-objview</tt></a> + (started with option <tt>-O</tt>) + and <a href="#using_rtfl_objcount"><tt>rtfl-objcount</tt></a>. + + <p>An example for the bypass mode is given in the section + <i><a href="tipsandtricks.html#transformation_of_attributes">Transformation + of attributes</a></i> in <i>Tips and tricks</i>.</p> + + <h2 id="scripts">Scripts</h2> + + <p>Part of the RTFL package are some scripts:</p> + + <ul> + <li><tt>rtfl-check-objects</tt>, which uses RTFL messages to + check for invalid object access,</li> + <li><tt>rtfl-filter-out-classes</tt>, which helps filtering out + classes not currently interesting (a function which should + become part + of <a href="#using_rtfl_objview"><tt>rtfl-objview</tt></a>),</li> + <li><tt>rtfl-objfilter</tt>, which filters messages by types, + aspects and priorities (as + <a href="#using_rtfl_objview"><tt>rtfl-objview</tt></a> + would do it),</li> + <li><tt>rtfl-objtail</tt>, which limits a stream of RTFL + messages, and</li> + <li><tt>rtfl-stacktraces</tt>, which prints stacktraces leading + to a specific method, or helps, as a filter, finding such + stack traces.</li> + </ul> + + <p>See the comments at the respective scripts for more information + on usage.</p> + + <p>The scripts <tt>rtfl-filter-out-classes</tt>, + <tt>rtfl-objfilter</tt>, <tt>rtfl-objtail</tt>, and (with + options <tt>-e</tt> and <tt>-m</tt>) <tt>rtfl-stacktraces</tt> + are used as filters: if e. g. you want to + use <tt>rtfl-objview</tt> but ignore all classes of the + package <tt>some::package</tt>, run</p> + + <p><tt><i>tested-program</i> | rtfl-filter-out-classes "some::package::*" | rtfl-objview [<i>options</i>]</tt></p> + + <p><strong>N. b.</strong> that since versioning and escaping of + protocols, parsing is incorrect:</p> + + <ul> + <li>The tag is checked as <tt>/^\[rtfl[^\]]*\]/</tt>, which + matches more as it should. (Fixed when using <tt>rtfl-objbase</tt>, + see below.)</li> + <li>More serious: the object module (prefix in the pre-version + protocol) is considered as optional; e. g. + <tt>(obj-)?create</tt> is used to cover both versions. This + may conflict with another module which also contains a + command <tt>create</tt>. (Fixed when using <tt>rtfl-objbase</tt>, + see below.)</li> + <li>Splitting up the line is done as before, ignoring quoting. + This works as long as there are no colons in parts where no + colons were allowed before (all parts except the last one). + For object identifiers etc., this will be still the case, but + for some parts (like method identifiers, which may in some + cases be fully qualified as <tt>Class::method</tt>), this may + cause problems in the future.</li> + <li>In one case quoting is supported: <tt>rtfl-objfilter</tt> + unquotes class names, replacing “\:” by “:” (but non “\\” by + “\”). This is done regardless of the protocol version, which + works as long as there are no backslashes in class names.</li> + </ul> + + <p>Furthermore, <a href="#using_rtfl_objbase"><tt>rtfl-objbase</tt></a> + should be used by all scripts to achieve a uniform handling of + <a href="#protocol_obj_ident"><tt>obj-ident</tt></a> and + <a href="#protocol_obj_delete"><tt>obj-delete</tt></a>, as well as + to fix some parsing problems. Currently, this has been done for + <tt>rtfl-filter-out-classes</tt> and <tt>rtfl-objtail</tt>.</p> + + <h2 id="see_also">See also</h2> + + <ul> + <li>RTFL website: + <a href="http://home.gna.org/rtfl/">http://home.gna.org/rtfl/</a></li> + <li><a href="tipsandtricks.html">Tips and tricks</a></li> + </ul> + + <hr/> + + <p><sup><a id="note-protocol-compat" href="#ref-protocol-compat">[1]</a></sup> + Some notes and definitions:</p> + + <p><em>Backward compatibility</em> means than a parser can focus + on one new version and ignore differences to older + versions. Backward compatibility is <em>not</em> generally + given, only within a major version; instead, a parser must be + aware of older versions. However, changes will be made in a way + making this as simple as possible; how this is done will be + decided from case to case.</p> + + <p><em>Forward compatibility</em> means than a parser implementing + an older version can deal with newer versions. Forward + compatibility is given within a major version, by defining the + following rules:</p> + + <ul> + <li>All lines with an unsupported <em>major</em> version for a + given protocol module must be ignored, as well as lines from + unsupported protocol modules.</li> + <li>For unsupported <em>minor</em> versions, + <ul> + <li>new commands and</li> + <li>unexpected arguments at the end</li> + </ul> + must be ignored.</li> + </ul> + + <p>This implies that there are two ways how a protocol module can + be extended in a forward compatible way (leading to a + new <em>minor</em> version):</p> + + <ul> + <li>adding new commands, and</li> + <li>adding new optional arguments (at the end) to existing + commands.</li> + </ul> + + <p>For simplicity, a parser may simply check the module + and <em>major</em> version, and, for supported modules and major + versions, ignore new commands and new arguments. New + forward-compatible changes can be implemented by and by.</p> + + <p><sup><a id="note-protocol-version" href="#ref-protocol-version">[2]</a></sup> + Currently, there is no version for the general part of the + protocol, which is independent of the modules. This may change + when the general protocol changes.</p> + + <p><sup><a id="note-mix-versions" href="#ref-mix-versions">[3]</a></sup> + Since a stream may consist of command lines of different + protocol modules, it can consist of command lines with different + versions. It is even allowed to mix different versions of the same + protocol module.</p> + + <p><sup><a id="note-old-protocol" href="#ref-old-protocol">[4]</a></sup> + + An old version of the protocol, called “pre-version protocol”, + is still supported for the objects module, albeit deprecated. + Lines start with <tt>[rtfl]</tt>, with neither module name nor + version; the module name is put before the command + (example: <tt>obj-create</tt>). No escaping mechanism is defined, so + that only the last part may contain colons. If not otherwise + stated, the specific commands are equivalent</p> + + <p><sup><a id="note-obj-color-old" href="#ref-obj-color-old">[5]</a></sup> + In the “pre-version protocol”, <tt>obj-color</tt> is still a + (deprecated) synonym of <tt>obj-class-color</tt>.</p> + + <p><sup><a id="note-obj-class-color-old" href="#ref-obj-class-color-old">[6]</a></sup> + In the “pre-version protocol”, the order of the arguments + is <tt>obj-class-color:<i>color</i>:<i>class</i></tt>, + since <i>class</i> typically contains colons, as used for C++ + namespaces, which are only allowed in the last argument. As for + the macro, there are different versions, so you have to be + cautious. (As opposed to the protocols, old versions of macros + are not supported, since the file <tt>debug_rtfl.hh</tt> is + typically copied into the tested program, and so the maintainer + of the tested program is responsible.) + </p> + + <p><sup><a id="note-class-color-specifity" href="#ref-class-color-specifity">[7]</a></sup> + The exact definition of the specifity of a pattern: number of + “?” − 100 ∙ number of “*” + 100 ∙ number of other + characters.</p> + + <p><sup><a id="note-class-color-specifity-old" href="#ref-class-color-specifity-old">[8]</a></sup> + In the “pre-version protocol”, the rule was different (the first + pattern was choosen), but for simplicity, newer versions of RTFL + treat the “pre-version protocol” like the new version.</p> + </body> +</html> diff --git a/doc/tipsandtricks.html b/doc/tipsandtricks.html new file mode 100644 index 0000000..92479f4 --- /dev/null +++ b/doc/tipsandtricks.html @@ -0,0 +1,415 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en"> + <head> + <title>RTFL: Tips and tricks</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + </head> + <body> + <p>[<a href="rtfl.html">Back to RTFL documentation.</a>]</p> + + <h1>Tips and tricks</h1> + + <p>Here, I'll summarize some useful experiences during the work + on <a href="http://www.dillo.org/">dillo</a>, for which RTFL was + initially written.</p> + + <h2>Preparing the tested program</h2> + + <ul> + <li>Do not prepare your program for a specific purpose, but in a + way that the RTFL message tell what happens in the + program, <em>in an objective way</em>. Ideally, most + preparation work is done once and pays off for many different + debugging problems.</li> + <li>Use different aspects so that filtering (as + in <a href="rtfl.html#using_rtfl_objview"><tt>rtfl-objview</tt></a>) + helps to focus on your specific problem. + </li> + </ul> + + <h2><a href="rtfl.html#using_rtfl_objview"><tt>rtfl-objview</tt></a></h2> + + <h3>Work around bugs and flaws</h3> + + <p>Currently, RTFL is not at the top of my priority list, but just + a mean to an end (with <a href="http://www.dillo.org/">dillo</a> + being the end). This means that I am not too motivated to fix + bugs which can be worked around:</p> + + <ul> + <li><del>There are several redrawing problems. Calling + <tt>xrefresh</tt> or switching between virtual screens should + should redraw everything correctly.</del> <i>(Redrawing + problems seem fixed now.)</i></li> + <li><del>Sometimes, the focused command is not visible. Pressing + Ctrl+N and then Ctrl+P should get it back into the viewport + again.</del> <i>(Scrolling problems seem fixed now.)</i></li> + <li>Use scripts to filter the RTFL messages. Hacking a script in + Perl is often much faster as integrating the functionality + into <tt>rtfl-objview</tt>. Some <a href="rtfl.html#scripts">useful + scripts</a> are already part of RTFL.</li> + </ul> + + <h3>General approach</h3> + + <p>Run the tested program and pass the standard output via pipe + to <tt>rtfl-objview</tt>. The + <a href="rtfl.html#rtfl_objview_command_line_options">command + line options</a> <tt>-M</tt> and <tt>-O</tt> are useful to get + an overview, <tt>-a</tt> and <tt>-A</tt> will help you to filter + out irrelevant messages.</p> + + <p>Then search for an entry point:</p> + + <ul> + <li>First of all, get an overview what the objects displayed in + the diagram actually represent. In + <a href="http://www.dillo.org/">dillo</a>, many objects + represent elements of the rendered document, so if an element + is rendered wrong, it is important to find the respective + object.</li> + <li>Depending on the tested program, it may be useful to search + for strange patterns of function calls. Simple example: + endless recursions are easily recognized by a steadily + increasing indentation.</li> + <li>An often helpful approach is to examine the attributes: + search for the respective object and the respective attribute; + a value which looks strange is a good starting point.</li> + </ul> + + <p>If you have troubles to find the respective object, try to + color it via + <a href="rtfl.html#protocol_obj_object_color">obj-object-color</a>, + using a filter like + this:<sup><a id="ref-pre-version-1" href="#note-pre-version-1">[1]</a></sup></p> + + <pre>sed 's/^\(\[rtfl-obj-1\.[0-9]*\].*\):set:\([^:]*\):words\.0\.text\/widget\/breakSpace:"Abc"$/\0\n\1:object-color:\2:#ff80ff/g'</pre> + + <p>which colors all objects to light purple which have the + attribute <tt>words.0.text/widget/breakSpace</tt> set to the + value <tt>"Abc"</tt>. This example is again taken + from <a href="http://www.dillo.org/">dillo</a>, like the + following one, which colors objects in the color in which they + are rendered (represented by the attribute + <tt>style.background-color</tt>):<sup><a id="ref-pre-version-2" href="#note-pre-version-2">[2]</a></sup></p> + + <pre>sed 's/^\(\[rtfl-obj-1\.[0-9]*\].*\):set:\([^:]*\):style.background-color:\(#[0-9a-f]*\)$/\0\n\1:object-color:\2:\3/g'</pre> + + <p>As soon as you have selected a specific command, try to + determine the commands before:</p> + + <ul> + <li>Navigate through the + <a href="rtfl.html#rtfl_objview_navigation">previous + commands</a>.</li> + <li>Or use the <a href="rtfl.html#rtfl_objview_stack_traces">stack + traces</a> function to get an overview.</li> + <li>One more tip: Use <i>Switch between related</i> for skipping + the messages within one method call if you think that the + problem is found outside of this method.</li> + </ul> + + <h3>Advanced problems</h3> + + <h4>CPU hogging</h4> + + <p>In this case, the number of messages (infinite) will be too + large for <tt>rtfl-objview</tt>. Simply use <tt>head(1)</tt> to + reduce them; for the number of lines, some trial and error is + necessary.</p> + + <h4>Too many messages</h4> + + <p>In some cases, it is useful to concentrate on what happens at + the end, especially if the program aborts. In this cases, the + script <a href="rtfl.html#scripts"><tt>rtfl-objtail</tt></a> + helps to filter messages, but preserve relevant object + creations, associations etc. at the beginning. Again, some tests + are needed for the number of lines.</p> + + <h4 id="transformation_of_attributes">Transformation of attributes</h4> + + <p>While <a href="rtfl.html#protocol_obj_set"><tt>obj-set</tt></a> + is used to set actual attributes, it is sometimes useful to + accumulate values. Instead of adding code to the tested program, + filters can do this and add respective pseudo attributes. The + following snippet counts for each object (and each process) the + number of calls of the method + <tt>draw</tt>:<sup><a id="ref-pre-version-3" href="#note-pre-version-3">[3]</a></sup></p> + + <pre><a href="rtfl.html#using_rtfl_tee">rtfl-tee</a> -b sh -c \ + "grep '^\[rtfl\].*:obj-enter:[^:]*:[^:]*:[^:]*:draw:' | \ + sed 's/^.*:\([^:]*\):obj-enter:\([^:]*\):.*$/\1:\2/g' | \ + sort | uniq -c | \ + sed 's/^ *\([^ ]*\) *\([^:]*\):\(.*\)$/[rtfl]:0:\2:obj-set:\3:<b>called <i>draw<\/i><\/b>:\1/g'"</pre> + + <h3>More tips</h3> + + <ul> + <li>Use the script + <a href="rtfl.html#scripts"><tt>rtfl-filter-out-classes</tt></a> + to reduce again messages. (I plan to integrate this + into <tt>rtfl-objview</tt>.)</li> + <li>The serial numbers preceding commands are often useful to + determine which command follows the other. E. g., by + comparing the numbers of attributes, it can be determined + which attribute possibly depends on another.</li> + </ul> + + <h2>Attic</h2> + + <p>Some ideas proved to be less practical that they looked at + first. Here, you will find several smaller projects which I have + abandoned, but you might find them useful as an inspiration.</p> + + <h3><a href="http://www.dillo.org/">Dillo</a>: HTML + attribute <tt>rtfl-id</tt></h3> + + <p>To make finding the exact relation between HTML elements of a + tested page and the <a href="http://www.dillo.org/">dillo</a> + widgets simpler, one could define a new HTML + element, <tt>rtfl-id</tt>, which is recognized by the HTML + parser of dillo and eventually passed to the respective + widget. (Printing a + <a href="rtfl.html#protocol_obj_set"><tt>obj-set</tt></a> + message is sufficient, storage in the widget is not necessary.) + This is demonstrated by the patch + <a href="#appendix_dillo_rtfl_id_diff"><tt>dillo-rtfl-id.diff</tt></a> + below.</p> + + <p>Furthermore, a simple Perl + script, <a href="#appendix_add_rtfl_id_pl"><tt>add-rtfl-id.pl</tt></a>, + adds unique <tt>rtfl-id</tt>'s to a given HTML page (standard + input to standard output).</p> + + <p>(<em>Warning:</em> both are incomplete.)</p> + + <h3 id="attic_rtfl_findrepeat"><tt>rtfl-findrepeat</tt></h3> + + <p><tt>Rtfl-findrepeat</tt> is a program which searches in a + stream for identical sequences. See comment at the beginning + of <tt>common/rtfl_findrepeat.cc</tt> for more details on + usage. Suitable arguments should be achieved by trial and + error.</p> + + <p>The idea behind this: if a program hogs the CPU, it is most + likely that it does similar things again and again, by + error. With a bit of luck, this will result in identical RTFL + messages, which <tt>rtfl-findrepeat</tt> helps to + find. With <a href="http://www.dillo.org/">dillo</a>, however, + this rendered not as really usable, so I have moved + <tt>rtfl-findrepeat</tt> to here.</p> + + <h2>Appendix</h2> + + <h3 id="appendix_dillo_rtfl_id_diff"><tt>dillo-rtfl-id.diff</tt></h3> + + <pre>diff -r 45a8d0d4b0d6 dw/widget.hh +--- a/dw/widget.hh Mon Sep 08 23:20:10 2014 +0200 ++++ b/dw/widget.hh Tue Sep 09 13:09:08 2014 +0200 +@@ -502,6 +502,11 @@ + */ + virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void removeChild (Widget *child); ++ ++ void setRtflId (const char *rtflId) { ++ if (rtflId) DBG_OBJ_SET_STR ("rtfl-id", rtflId); ++ else DBG_OBJ_SET_SYM ("rtfl-id", "none"); ++ } + }; + + void splitHeightPreserveAscent (int height, int *ascent, int *descent); +diff -r 45a8d0d4b0d6 src/doctree.hh +--- a/src/doctree.hh Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/doctree.hh Tue Sep 09 13:09:08 2014 +0200 +@@ -13,6 +13,7 @@ + lout::misc::SimpleVector<char*> *klass; + const char *pseudo; + const char *id; ++ const char *rtflId; + + DoctreeNode () { + parent = NULL; +@@ -21,11 +22,13 @@ + klass = NULL; + pseudo = NULL; + id = NULL; ++ rtflId = NULL; + element = 0; + }; + + ~DoctreeNode () { + dFree ((void*) id); ++ dFree ((void*) rtflId); + while (lastChild) { + DoctreeNode *n = lastChild; + lastChild = lastChild->sibling; +diff -r 45a8d0d4b0d6 src/form.cc +--- a/src/form.cc Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/form.cc Tue Sep 09 13:09:08 2014 +0200 +@@ -938,6 +938,7 @@ + * but it caused 100% CPU usage. + */ + page = new Textblock (false); ++ page->setRtflId (html->rtflId ()); + page->setStyle (html->backgroundStyle ()); + + ResourceFactory *factory = HT2LT(html)->getResourceFactory(); +diff -r 45a8d0d4b0d6 src/html.cc +--- a/src/html.cc Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/html.cc Tue Sep 09 13:09:08 2014 +0200 +@@ -362,6 +362,7 @@ + static void Html_add_textblock(DilloHtml *html, bool addBreaks, int breakSpace) + { + Textblock *textblock = new Textblock (prefs.limit_text_width); ++ textblock->setRtflId (html->rtflId ()); + + if (addBreaks) + HT2TB(html)->addParbreak (breakSpace, html->wordStyle ()); +@@ -2186,6 +2187,7 @@ + } + + dw::Image *dw = new dw::Image(alt_ptr); ++ dw->setRtflId (html->rtflId ()); + image = + a_Image_new(html->dw->getLayout(), (void*)(dw::core::ImgRenderer*)dw, 0); + +@@ -3038,6 +3040,7 @@ + HT2TB(html)->addParbreak (5, html->wordStyle ()); + + hruler = new Ruler(); ++ hruler->setRtflId (html->rtflId ()); + hruler->setStyle (html->style ()); + HT2TB(html)->addWidget (hruler, html->style ()); + HT2TB(html)->addParbreak (5, html->wordStyle ()); +@@ -3783,6 +3786,10 @@ + const char *attrbuf; + char lang[3]; + ++ if (tagsize >= 13 && /* length of "<t rtfl-id=i>" */ ++ (attrbuf = a_Html_get_attr(html, tag, tagsize, "rtfl-id"))) ++ html->styleEngine->setRtflId(attrbuf); ++ + if (tagsize >= 8 && /* length of "<t id=i>" */ + (attrbuf = a_Html_get_attr(html, tag, tagsize, "id"))) { + /* According to the SGML declaration of HTML 4, all NAME values +@@ -3882,6 +3889,7 @@ + HT2TB(html)->addParbreak (0, wordStyle); + + list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width); ++ list_item->setRtflId (html->rtflId ()); + HT2TB(html)->addWidget (list_item, style); + HT2TB(html)->addParbreak (0, wordStyle); + *ref_list_item = list_item; +diff -r 45a8d0d4b0d6 src/html_common.hh +--- a/src/html_common.hh Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/html_common.hh Tue Sep 09 13:09:08 2014 +0200 +@@ -234,6 +234,7 @@ + + inline void restyle () { styleEngine->restyle (bw); } + ++ inline const char *rtflId () { return styleEngine->rtflId (); } + }; + + /* +diff -r 45a8d0d4b0d6 src/styleengine.cc +--- a/src/styleengine.cc Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/styleengine.cc Tue Sep 09 13:09:08 2014 +0200 +@@ -160,6 +160,12 @@ + dn->id = dStrdup (id); + } + ++void StyleEngine::setRtflId (const char *rtflId) { ++ DoctreeNode *dn = doctree->top (); ++ assert (dn->rtflId == NULL); ++ dn->rtflId = dStrdup (rtflId); ++} ++ + /** + * \brief split a string at sep chars and return a SimpleVector of strings + */ +diff -r 45a8d0d4b0d6 src/styleengine.hh +--- a/src/styleengine.hh Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/styleengine.hh Tue Sep 09 13:09:08 2014 +0200 +@@ -77,6 +77,7 @@ + void startElement (int tag, BrowserWindow *bw); + void startElement (const char *tagname, BrowserWindow *bw); + void setId (const char *id); ++ void setRtflId (const char *rtflId); + const char * getId () { return doctree->top ()->id; }; + void setClass (const char *klass); + void setStyle (const char *style); +@@ -122,6 +123,10 @@ + else + return wordStyle0 (bw); + }; ++ ++ inline const char *rtflId () { ++ return stack->getRef(stack->size()-1)->doctreeNode->rtflId; ++ }; + }; + + #endif +diff -r 45a8d0d4b0d6 src/table.cc +--- a/src/table.cc Mon Sep 08 23:20:10 2014 +0200 ++++ b/src/table.cc Tue Sep 09 13:09:08 2014 +0200 +@@ -158,6 +158,7 @@ + + HT2TB(html)->addParbreak (0, html->wordStyle ()); + table = new dw::Table(prefs.limit_text_width); ++ table->setRtflId (html->rtflId ()); + HT2TB(html)->addWidget (table, html->style ()); + HT2TB(html)->addParbreak (0, html->wordStyle ()); + +@@ -451,6 +452,7 @@ + prefs.limit_text_width); + else + col_tb = new SimpleTableCell (prefs.limit_text_width); ++ col_tb->setRtflId (html->rtflId ()); + + if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){ + Html_set_collapsing_border_model(html, col_tb);</pre> + + <h3 id="appendix_add_rtfl_id_pl"><tt>add-rtfl-id.pl</tt></h3> + + <pre>#!/usr/bin/perl + +$id = 0; + +while(<STDIN>) { + chomp; + $first = 1; + foreach $part (split /w</) { + if ($first) { + print $part; + } else { + if ($part =~ /^\//) { + print "<$part"; + } else { + $part =~ s/([^ >]*)/\1 rtfl-id="$id"/; + $id++; + print "<$part"; + } + } + $first = 0; + } + print "\n"; +}</pre> + + <hr/> + + <p><sup><a id="note-pre-version-1" href="#ref-pre-version-1">[1]</a></sup> + Use this for the pre-version protocol:</p> + <pre>sed 's/^\(\[rtfl\].*\):obj-set:\([^:]*\):words\.0\.text\/widget\/breakSpace:"Abc"$/\0\n\1:obj-object-color:\2:#ff80ff/g'</pre> + + <p><sup><a id="note-pre-version-2" href="#ref-pre-version-2">[2]</a></sup> + Again, for the pre-version protocol:</p> + <pre>sed 's/^\(\[rtfl\].*\):obj-set:\([^:]*\):style.background-color:\(#[0-9a-f]*\)$/\0\n\1:obj-object-color:\2:\3/g'</pre> + + <p><sup><a id="note-pre-version-3" href="#ref-pre-version-3">[3]</a></sup> + This works only with the pre-version protocol. Should be + adjusted eventually.</p> + + </body> +</html> diff --git a/dw/Makefile.am b/dw/Makefile.am new file mode 100644 index 0000000..471a309 --- /dev/null +++ b/dw/Makefile.am @@ -0,0 +1,57 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DDILLO_LIBDIR='"$(pkglibdir)/"' \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dw"' + +noinst_LIBRARIES = \ + libDw-core.a \ + libDw-fltk.a + +libDw_core_a_SOURCES = \ + core.hh \ + events.hh \ + findtext.cc \ + findtext.hh \ + imgbuf.hh \ + imgrenderer.hh \ + imgrenderer.cc \ + iterator.cc \ + iterator.hh \ + layout.cc \ + layout.hh \ + platform.hh \ + selection.hh \ + selection.cc \ + style.cc \ + style.hh \ + types.cc \ + types.hh \ + ui.cc \ + ui.hh \ + view.hh \ + widget.cc \ + widget.hh + +# "fltkcomplexbutton.cc", "fltkcomplexbutton.hh", "fltkflatview.cc", +# and "fltkflatview.hh" have been removed from libDw-fltk.a. + +libDw_fltk_a_SOURCES = \ + fltkcore.hh \ + fltkimgbuf.cc \ + fltkimgbuf.hh \ + fltkmisc.cc \ + fltkmisc.hh \ + fltkplatform.cc \ + fltkplatform.hh \ + fltkpreview.hh \ + fltkpreview.cc \ + fltkui.cc \ + fltkui.hh \ + fltkviewbase.cc \ + fltkviewbase.hh \ + fltkviewport.cc \ + fltkviewport.hh + +libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@ + +EXTRA_DIST = preview.xbm diff --git a/dw/core.hh b/dw/core.hh new file mode 100644 index 0000000..022574b --- /dev/null +++ b/dw/core.hh @@ -0,0 +1,60 @@ +#ifndef __DW_CORE_HH__ +#define __DW_CORE_HH__ + +#define __INCLUDED_FROM_DW_CORE_HH__ + +/** + * \brief Dw is in this namespace, or sub namespaces of this one. + * + * The core can be found in dw::core, widgets are defined directly here. + * + * \sa \ref dw-overview + */ +namespace dw { + +/** + * \brief The core of Dw is defined in this namespace. + * + * \sa \ref dw-overview + */ +namespace core { + +typedef unsigned char byte; + +class Layout; +class View; +class Widget; +class Iterator; + +// Nothing yet to free. +inline void freeall () { } + +namespace ui { + +class ResourceFactory; + +} // namespace ui +} // namespace core +} // namespace dw + +#include "../lout/object.hh" +#include "../lout/container.hh" +#include "../lout/signal.hh" + +#include "types.hh" +#include "events.hh" +#include "imgbuf.hh" +#include "imgrenderer.hh" +#include "style.hh" +#include "view.hh" +#include "platform.hh" +#include "iterator.hh" +#include "findtext.hh" +#include "selection.hh" +#include "layout.hh" +#include "widget.hh" +#include "ui.hh" + +#undef __INCLUDED_FROM_DW_CORE_HH__ + +#endif // __DW_CORE_HH__ diff --git a/dw/events.hh b/dw/events.hh new file mode 100644 index 0000000..5309186 --- /dev/null +++ b/dw/events.hh @@ -0,0 +1,83 @@ +#ifndef __DW_EVENTS_HH__ +#define __DW_EVENTS_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief Platform independent representation. + */ +enum ButtonState +{ + /* We won't use more than these ones. */ + SHIFT_MASK = 1 << 0, + CONTROL_MASK = 1 << 1, + META_MASK = 1 << 2, + BUTTON1_MASK = 1 << 3, + BUTTON2_MASK = 1 << 4, + BUTTON3_MASK = 1 << 5 +}; + +/** + * \brief Base class for all events. + * + * The dw::core::Event hierarchy describes events in a platform independent + * way. + */ +class Event: public lout::object::Object +{ +public: +}; + +/** + * \brief Base class for all mouse events. + */ +class MouseEvent: public Event +{ +public: + ButtonState state; +}; + +/** + * \brief Base class for all mouse events related to a specific position. + */ +class MousePositionEvent: public MouseEvent +{ +public: + int xCanvas, yCanvas, xWidget, yWidget; +}; + +/** + * \brief Represents a button press or release event. + */ +class EventButton: public MousePositionEvent +{ +public: + int numPressed; /* 1 for simple click, 2 for double click, etc. */ + int button; +}; + +/** + * \brief Represents a mouse motion event. + */ +class EventMotion: public MousePositionEvent +{ +}; + +/** + * \brief Represents a enter or leave notify event. + */ +class EventCrossing: public MouseEvent +{ +public: + Widget *lastWidget, *currentWidget; +}; + +} // namespace core +} // namespace dw + +#endif // __DW_EVENTS_HH__ diff --git a/dw/findtext.cc b/dw/findtext.cc new file mode 100644 index 0000000..57c83c5 --- /dev/null +++ b/dw/findtext.cc @@ -0,0 +1,307 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include "../lout/debug.hh" +#include "../lout/msg.h" + +namespace dw { +namespace core { + +FindtextState::FindtextState () +{ + DBG_OBJ_CREATE ("dw::core::FindtextState"); + + key = NULL; + nexttab = NULL; + widget = NULL; + iterator = NULL; + hlIterator = NULL; +} + +FindtextState::~FindtextState () +{ + if (key) + free(key); + if (nexttab) + delete[] nexttab; + if (iterator) + delete iterator; + if (hlIterator) + delete hlIterator; + + DBG_OBJ_DELETE (); +} + +void FindtextState::setWidget (Widget *widget) +{ + this->widget = widget; + + // A widget change will restart the search. + if (key) + free(key); + key = NULL; + if (nexttab) + delete[] nexttab; + nexttab = NULL; + + if (iterator) + delete iterator; + iterator = NULL; + if (hlIterator) + delete hlIterator; + hlIterator = NULL; +} + +FindtextState::Result FindtextState::search (const char *key, bool caseSens, + bool backwards) +{ + if (!widget || *key == 0) // empty keys are not found + return NOT_FOUND; + + bool wasHighlighted = unhighlight (); + bool newKey; + + // If the key (or the widget) changes (including case sensitivity), + // the search is started from the beginning. + if (this->key == NULL || this->caseSens != caseSens || + strcmp (this->key, key) != 0) { + newKey = true; + if (this->key) + free(this->key); + this->key = strdup (key); + this->caseSens = caseSens; + + if (nexttab) + delete[] nexttab; + nexttab = createNexttab (key, caseSens, backwards); + + if (iterator) + delete iterator; + iterator = new CharIterator (widget, true); + + if (backwards) { + /* Go to end */ + while (iterator->next () ) ; + iterator->prev (); //We don't want to be at CharIterator::END. + } else { + iterator->next (); + } + } else + newKey = false; + + bool firstTrial = !wasHighlighted || newKey; + + if (search0 (backwards, firstTrial)) { + // Highlighting is done with a clone. + hlIterator = iterator->cloneCharIterator (); + for (int i = 0; key[i]; i++) + hlIterator->next (); + CharIterator::highlight (iterator, hlIterator, HIGHLIGHT_FINDTEXT); + CharIterator::scrollTo (iterator, hlIterator, + HPOS_INTO_VIEW, VPOS_CENTER); + + // The search will continue from the word after the found position. + iterator->next (); + return SUCCESS; + } else { + if (firstTrial) { + return NOT_FOUND; + } else { + // Nothing found anymore, reset the state for the next trial. + delete iterator; + iterator = new CharIterator (widget, true); + if (backwards) { + /* Go to end */ + while (iterator->next ()) ; + iterator->prev (); //We don't want to be at CharIterator::END. + } else { + iterator->next (); + } + // We expect a success. + Result result2 = search (key, caseSens, backwards); + assert (result2 == SUCCESS); + return RESTART; + } + } +} + +/** + * \brief This method is called when the user closes the "find text" dialog. + */ +void FindtextState::resetSearch () +{ + unhighlight (); + + if (key) + free(key); + key = NULL; +} + +/* + * Return a new string: with the reverse of the original. + */ +const char* FindtextState::rev(const char *str) +{ + if (!str) + return NULL; + + int len = strlen(str); + char *nstr = new char[len+1]; + for (int i = 0; i < len; ++i) + nstr[i] = str[len-1 -i]; + nstr[len] = 0; + + return nstr; +} + +int *FindtextState::createNexttab (const char *needle, bool caseSens, + bool backwards) +{ + const char* key; + + key = (backwards) ? rev(needle) : needle; + int i = 0; + int j = -1; + int l = strlen (key); + int *nexttab = new int[l + 1]; // + 1 is necessary for l == 1 case + nexttab[0] = -1; + + do { + if (j == -1 || charsEqual (key[i], key[j], caseSens)) { + i++; + j++; + nexttab[i] = j; + //_MSG ("nexttab[%d] = %d\n", i, j); + } else + j = nexttab[j]; + } while (i < l - 1); + + if (backwards) + delete [] key; + + return nexttab; +} + +/** + * \brief Unhighlight, and return whether a region was highlighted. + */ +bool FindtextState::unhighlight () +{ + if (hlIterator) { + CharIterator *start = hlIterator->cloneCharIterator (); + for (int i = 0; key[i]; i++) + start->prev (); + + CharIterator::unhighlight (start, hlIterator, HIGHLIGHT_FINDTEXT); + delete start; + delete hlIterator; + hlIterator = NULL; + + return true; + } else + return false; +} + +bool FindtextState::search0 (bool backwards, bool firstTrial) +{ + if (iterator->getChar () == CharIterator::END) + return false; + + bool ret = false; + const char* searchKey = (backwards) ? rev(key) : key; + int j = 0; + bool nextit = true; + int l = strlen (key); + + if (backwards && !firstTrial) { + _MSG("Having to do."); + /* Position correctly */ + /* In order to achieve good results (i.e: find a word that ends within + * the previously searched word's limit) we have to position the + * iterator in the semilast character of the previously searched word. + * + * Since we know that if a word was found before it was exactly the + * same word as the one we are searching for now, we can apply the + * following expression: + * + * Where l=length of the key and n=num of positions to move: + * + * n = l - 3 + * + * If n is negative, we have to move backwards, but if it is + * positive, we have to move forward. So, when l>=4, we start moving + * the iterator forward. */ + + if (l==1) { + iterator->prev(); + iterator->prev(); + } else if (l==2) { + iterator->prev(); + } else if (l>=4) { + for (int i=0; i<l-3; i++) { + iterator->next(); + } + } + + } else if (backwards && l==1) { + /* Particular case where we can't find the last character */ + iterator->next(); + } + + do { + if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) { + j++; + nextit = backwards ? iterator->prev () : iterator->next (); + } else + j = nexttab[j]; + } while (nextit && j < l); + + if (j >= l) { + if (backwards) { + //This is the location of the key + iterator->next(); + } else { + // Go back to where the key was found. + for (int i = 0; i < l; i++) + iterator->prev (); + } + ret = true; + } + + if (backwards) + delete [] searchKey; + + return ret; +} + +} // namespace core +} // namespace dw diff --git a/dw/findtext.hh b/dw/findtext.hh new file mode 100644 index 0000000..c680348 --- /dev/null +++ b/dw/findtext.hh @@ -0,0 +1,84 @@ +#ifndef __DW_FINDTEXT_STATE_H__ +#define __DW_FINDTEXT_STATE_H__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include <ctype.h> + +namespace dw { +namespace core { + +class FindtextState +{ +public: + typedef enum { + /** \brief The next occurrence of the pattern has been found. */ + SUCCESS, + + /** + * \brief There is no further occurrence of the pattern, instead, the + * first occurrence has been selected. + */ + RESTART, + + /** \brief The patten does not at all occur in the text. */ + NOT_FOUND + } Result; + +private: + /** + * \brief The key used for the last search. + * + * If dw::core::Findtext::search is called with the same key, the search + * is continued, otherwise it is restarted. + */ + char *key; + + /** \brief Whether the last search was case sensitive. */ + bool caseSens; + + /** \brief The table used for KMP search. */ + int *nexttab; + + /** \brief The top of the widget tree, in which the search is done. + * + * From this, the iterator will be constructed. Set by + * dw::core::Findtext::widget + */ + Widget *widget; + + /** \brief The position from where the next search will start. */ + CharIterator *iterator; + + /** + * \brief The position from where the characters are highlighted. + * + * NULL, when no text is highlighted. + */ + CharIterator *hlIterator; + + static const char* rev(const char* _str); /* reverse a C string */ + + static int *createNexttab (const char *needle,bool caseSens,bool backwards); + bool unhighlight (); + bool search0 (bool backwards, bool firstTrial); + + inline static bool charsEqual (char c1, char c2, bool caseSens) + { return caseSens ? c1 == c2 : tolower (c1) == tolower (c2) || + (isspace (c1) && isspace (c2)); } + +public: + FindtextState (); + ~FindtextState (); + + void setWidget (Widget *widget); + Result search (const char *key, bool caseSens, bool backwards); + void resetSearch (); +}; + +} // namespace core +} // namespace dw + +#endif // __DW_FINDTEXT_STATE_H__ diff --git a/dw/fltkcore.hh b/dw/fltkcore.hh new file mode 100644 index 0000000..5ac619b --- /dev/null +++ b/dw/fltkcore.hh @@ -0,0 +1,36 @@ +#ifndef __DW_FLTK_CORE_HH__ +#define __DW_FLTK_CORE_HH__ + +#define __INCLUDED_FROM_DW_FLTK_CORE_HH__ + +namespace dw { +namespace fltk { +namespace ui { + +class FltkResource; + +} // namespace ui +} // namespace fltk +} // namespace dw + +#include <FL/Fl_Widget.H> + +#include "core.hh" +#include "fltkimgbuf.hh" +#include "fltkplatform.hh" +#include "fltkui.hh" + +namespace dw { +namespace fltk { + +inline void freeall () +{ + FltkImgbuf::freeall (); +} + +} // namespace fltk +} // namespace dw + +#undef __INCLUDED_FROM_DW_FLTK_CORE_HH__ + +#endif // __DW_FLTK_CORE_HH__ diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc new file mode 100644 index 0000000..6621dc5 --- /dev/null +++ b/dw/fltkimgbuf.cc @@ -0,0 +1,584 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007, 2012-2013 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "fltkcore.hh" +#include "../lout/msg.h" +#include "../lout/misc.hh" + +#include <FL/fl_draw.H> +#include <math.h> + +#define IMAGE_MAX_AREA (6000 * 6000) + +#define MAX_WIDTH 0x8000 +#define MAX_HEIGHT 0x8000 + +namespace dw { +namespace fltk { + +using namespace lout::container::typed; + +const enum ScaleMode { SIMPLE, BEAUTIFUL, BEAUTIFUL_GAMMA } + scaleMode = BEAUTIFUL_GAMMA; + +Vector <FltkImgbuf::GammaCorrectionTable> *FltkImgbuf::gammaCorrectionTables + = new Vector <FltkImgbuf::GammaCorrectionTable> (true, 2); + +uchar *FltkImgbuf::findGammaCorrectionTable (double gamma) +{ + // Since the number of possible keys is low, a linear search is + // sufficiently fast. + + for (int i = 0; i < gammaCorrectionTables->size(); i++) { + GammaCorrectionTable *gct = gammaCorrectionTables->get(i); + if (gct->gamma == gamma) + return gct->map; + } + + _MSG("Creating new table for gamma = %g\n", gamma); + + GammaCorrectionTable *gct = new GammaCorrectionTable(); + gct->gamma = gamma; + + for (int i = 0; i < 256; i++) + gct->map[i] = 255 * pow((double)i / 255, gamma); + + gammaCorrectionTables->put (gct); + return gct->map; +} + +bool FltkImgbuf::excessiveImageDimensions (int width, int height) +{ + return width <= 0 || height <= 0 || + width > IMAGE_MAX_AREA / height; +} + +void FltkImgbuf::freeall () +{ + _MSG("Deleting gammaCorrectionTables\n"); + delete gammaCorrectionTables; + gammaCorrectionTables = NULL; +} + +FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma) +{ + DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf"); + + _MSG ("FltkImgbuf::FltkImgbuf: new root %p\n", this); + init (type, width, height, gamma, NULL); +} + +FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma, + FltkImgbuf *root) +{ + DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf"); + + _MSG ("FltkImgbuf::FltkImgbuf: new scaled %p, root is %p\n", this, root); + init (type, width, height, gamma, root); +} + +void FltkImgbuf::init (Type type, int width, int height, double gamma, + FltkImgbuf *root) +{ + if (excessiveImageDimensions (width, height)) { + // Excessive image sizes which would cause crashes due to too + // big allocations for the image buffer (for root buffers, when + // the image was specially prepared). In this case we use a 1 x + // 1 size. + MSG("FltkImgbuf::init: suspicious image size request %d x %d\n", + width, height); + init (type, 1, 1, gamma, root); + } else if (width > MAX_WIDTH) { + // Too large dimensions cause dangerous overflow errors, so we + // limit dimensions to harmless values. + // + // Example: 65535 * 65536 / 65536 (see scaling below) results in + // the negative value -1. + + MSG("FltkImgbuf::init: cannot handle large width %d\n", width); + init (type, MAX_WIDTH, height, gamma, root); + } else if (height > MAX_HEIGHT) { + MSG("FltkImgbuf::init: cannot handle large height %d\n", height); + init (type, width, MAX_HEIGHT, gamma, root); + } else if (gamma <= 0) { + MSG("FltkImgbuf::init: non-positive gamma %g\n", gamma); + init (type, width, height, 1, root); + } else { + this->root = root; + this->type = type; + this->width = width; + this->height = height; + this->gamma = gamma; + + DBG_OBJ_SET_NUM ("width", width); + DBG_OBJ_SET_NUM ("height", height); + + // TODO: Maybe this is only for root buffers + switch (type) { + case RGBA: bpp = 4; break; + case RGB: bpp = 3; break; + default: bpp = 1; break; + } + _MSG("FltkImgbuf::init this=%p width=%d height=%d bpp=%d gamma=%g\n", + this, width, height, bpp, gamma); + rawdata = new uchar[bpp * width * height]; + // Set light-gray as interim background color. + memset(rawdata, 222, width*height*bpp); + + refCount = 1; + deleteOnUnref = true; + copiedRows = new lout::misc::BitSet (height); + + // The list is only used for root buffers. + if (isRoot()) + scaledBuffers = new lout::container::typed::List <FltkImgbuf> (true); + else + scaledBuffers = NULL; + + if (!isRoot()) { + // Scaling + for (int row = 0; row < root->height; row++) { + if (root->copiedRows->get (row)) + scaleRow (row, root->rawdata + row*root->width*root->bpp); + } + } + } +} + +FltkImgbuf::~FltkImgbuf () +{ + _MSG ("FltkImgbuf::~FltkImgbuf\n"); + + if (!isRoot()) + root->detachScaledBuf (this); + + delete[] rawdata; + delete copiedRows; + + if (scaledBuffers) + delete scaledBuffers; + + DBG_OBJ_DELETE (); +} + +/** + * \brief This method is called for the root buffer, when a scaled buffer + * removed. + */ +void FltkImgbuf::detachScaledBuf (FltkImgbuf *scaledBuf) +{ + scaledBuffers->detachRef (scaledBuf); + + _MSG("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n", + this, scaledBuf, scaledBuffers->size ()); + + if (refCount == 0 && scaledBuffers->isEmpty () && deleteOnUnref) + // If the root buffer is not used anymore, but this is the last scaled + // buffer. + // See also: FltkImgbuf::unref(). + delete this; +} + +void FltkImgbuf::setCMap (int *colors, int num_colors) +{ +} + +inline void FltkImgbuf::scaleRow (int row, const core::byte *data) +{ + if (row < root->height) { + if (scaleMode == SIMPLE) + scaleRowSimple (row, data); + else + scaleRowBeautiful (row, data); + } +} + +inline void FltkImgbuf::scaleRowSimple (int row, const core::byte *data) +{ + int sr1 = scaledY (row); + int sr2 = scaledY (row + 1); + + for (int sr = sr1; sr < sr2; sr++) { + // Avoid multiple passes. + if (copiedRows->get(sr)) continue; + + copiedRows->set (sr, true); + if (sr == sr1) { + for (int px = 0; px < root->width; px++) { + int px1 = px * width / root->width; + int px2 = (px+1) * width / root->width; + for (int sp = px1; sp < px2; sp++) { + memcpy(rawdata + (sr*width + sp)*bpp, data + px*bpp, bpp); + } + } + } else { + memcpy(rawdata + sr*width*bpp, rawdata + sr1*width*bpp, width*bpp); + } + } +} + +inline void FltkImgbuf::scaleRowBeautiful (int row, const core::byte *data) +{ + int sr1 = scaledY (row); + int sr2 = scaledY (row + 1); + bool allRootRows = false; + + // Don't rescale rows! + if (copiedRows->get(sr1)) return; + + if (height > root->height) { + scaleBuffer (data, root->width, 1, + rawdata + sr1 * width * bpp, width, sr2 - sr1, + bpp, gamma); + // Mark scaled rows done + for (int sr = sr1; sr < sr2 || sr == sr1; sr++) + copiedRows->set (sr, true); + } else { + assert (sr1 == sr2 || sr1 + 1 == sr2); + int row1 = backscaledY(sr1), row2 = backscaledY(sr1 + 1); + + // Check all the necessary root lines already arrived, + // a larger area than a single row may be accessed here. + for (int r=row1; (allRootRows=root->copiedRows->get(r)) && ++r < row2; ); + if (allRootRows) { + scaleBuffer (root->rawdata + row1 * root->width * bpp, + root->width, row2 - row1, + rawdata + sr1 * width * bpp, width, 1, + bpp, gamma); + // Mark scaled row done + copiedRows->set (sr1, true); + } + } +} + +/** + * General method to scale an image buffer. Used to scale single lines + * in scaleRowBeautiful. + * + * The algorithm is rather simple. If the scaled buffer is smaller + * (both width and height) than the original buffer, each pixel in the + * scaled buffer is assigned a rectangle of pixels in the original + * buffer; the resulting pixel value (red, green, blue) is simply the + * average of all pixel values. This is pretty fast and leads to + * rather good results. + * + * Nothing special (like interpolation) is done when scaling up. + * + * If scaleMode is set to BEAUTIFUL_GAMMA, gamma correction is + * considered, see <http://www.4p8.com/eric.brasseur/gamma.html>. + * + * TODO Could be optimized as in scaleRowSimple: when the destination + * image is larger, calculate only one row/column, and copy it to the + * other rows/columns. + */ +inline void FltkImgbuf::scaleBuffer (const core::byte *src, int srcWidth, + int srcHeight, core::byte *dest, + int destWidth, int destHeight, int bpp, + double gamma) +{ + uchar *gammaMap1, *gammaMap2; + + if (scaleMode == BEAUTIFUL_GAMMA) { + gammaMap1 = findGammaCorrectionTable (gamma); + gammaMap2 = findGammaCorrectionTable (1 / gamma); + } + + for(int x = 0; x < destWidth; x++) + for(int y = 0; y < destHeight; y++) { + int xo1 = x * srcWidth / destWidth; + int xo2 = lout::misc::max ((x + 1) * srcWidth / destWidth, xo1 + 1); + int yo1 = y * srcHeight / destHeight; + int yo2 = lout::misc::max ((y + 1) * srcHeight / destHeight, yo1 + 1); + int n = (xo2 - xo1) * (yo2 - yo1); + + int v[bpp]; + for(int i = 0; i < bpp; i++) + v[i] = 0; + + for(int xo = xo1; xo < xo2; xo++) + for(int yo = yo1; yo < yo2; yo++) { + const core::byte *ps = src + bpp * (yo * srcWidth + xo); + for(int i = 0; i < bpp; i++) + v[i] += + (scaleMode == BEAUTIFUL_GAMMA ? gammaMap2[ps[i]] : ps[i]); + } + + core::byte *pd = dest + bpp * (y * destWidth + x); + for(int i = 0; i < bpp; i++) + pd[i] = + scaleMode == BEAUTIFUL_GAMMA ? gammaMap1[v[i] / n] : v[i] / n; + } +} + +void FltkImgbuf::copyRow (int row, const core::byte *data) +{ + assert (isRoot()); + + if (row < height) { + // Flag the row done and copy its data. + copiedRows->set (row, true); + memcpy(rawdata + row * width * bpp, data, width * bpp); + + // Update all the scaled buffers of this root image. + for (Iterator <FltkImgbuf> it = scaledBuffers->iterator(); + it.hasNext(); ) { + FltkImgbuf *sb = it.getNext (); + sb->scaleRow (row, data); + } + } +} + +void FltkImgbuf::newScan () +{ + if (isRoot()) { + for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext();){ + FltkImgbuf *sb = it.getNext (); + sb->copiedRows->clear(); + } + } +} + +core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height) +{ + if (!isRoot()) + return root->getScaledBuf (width, height); + + if (width > MAX_WIDTH) { + // Similar to init. + MSG("FltkImgbuf::getScaledBuf: cannot handle large width %d\n", width); + return getScaledBuf (MAX_WIDTH, height); + } + if (height > MAX_HEIGHT) { + MSG("FltkImgbuf::getScaledBuf: cannot handle large height %d\n", height); + return getScaledBuf (width, MAX_HEIGHT); + } + + if (width == this->width && height == this->height) { + ref (); + return this; + } + + for (Iterator <FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext(); ) { + FltkImgbuf *sb = it.getNext (); + if (sb->width == width && sb->height == height) { + sb->ref (); + return sb; + } + } + + // Check for excessive image sizes which would cause crashes due to + // too big allocations for the image buffer. In this case we return + // a pointer to the unscaled image buffer. + if (excessiveImageDimensions (width, height)) { + MSG("FltkImgbuf::getScaledBuf: suspicious image size request %d x %d\n", + width, height); + ref (); + return this; + } + + // This size is not yet used, so a new buffer has to be created. + FltkImgbuf *sb = new FltkImgbuf (type, width, height, gamma, this); + scaledBuffers->append (sb); + DBG_OBJ_ASSOC_CHILD (sb); + + return sb; +} + +void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area) +{ + // TODO: May have to be adjusted. + + if (isRoot()) { + /* root buffer */ + area->x = 0; + area->y = row; + area->width = width; + area->height = 1; + _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n", + area->x, area->y, area->width, area->height); + } else { + if (row > root->height) + area->x = area->y = area->width = area->height = 0; + else { + // scaled buffer + int sr1 = scaledY (row); + int sr2 = scaledY (row + 1); + + area->x = 0; + area->y = sr1; + area->width = width; + area->height = sr2 - sr1; + _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n", + area->x, area->y, area->width, area->height); + } + } +} + +int FltkImgbuf::getRootWidth () +{ + return root ? root->width : width; +} + +int FltkImgbuf::getRootHeight () +{ + return root ? root->height : height; +} + +core::Imgbuf *FltkImgbuf::createSimilarBuf (int width, int height) +{ + return new FltkImgbuf (type, width, height, gamma); +} + +void FltkImgbuf::copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc) +{ + FltkImgbuf *fDest = (FltkImgbuf*)dest; + assert (bpp == fDest->bpp); + + int xSrc2 = lout::misc::min (xSrc + widthSrc, fDest->width - xDestRoot); + int ySrc2 = lout::misc::min (ySrc + heightSrc, fDest->height - yDestRoot); + + //printf ("copying from (%d, %d), %d x %d to (%d, %d) (root) => " + // "xSrc2 = %d, ySrc2 = %d\n", + // xSrc, ySrc, widthSrc, heightSrc, xDestRoot, yDestRoot, + // xSrc2, ySrc2); + + for (int x = xSrc; x < xSrc2; x++) + for (int y = ySrc; y < ySrc2; y++) { + int iSrc = x + width * y; + int iDest = xDestRoot + x + fDest->width * (yDestRoot + y); + + //printf (" (%d, %d): %d -> %d\n", x, y, iSrc, iDest); + + for (int b = 0; b < bpp; b++) + fDest->rawdata[bpp * iDest + b] = rawdata[bpp * iSrc + b]; + } +} + +void FltkImgbuf::ref () +{ + refCount++; + + //if (root) + // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", + // this, root, refCount); + //else + // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount); +} + +void FltkImgbuf::unref () +{ + //if (root) + // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", + // this, root, refCount - 1); + //else + // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1); + + if (--refCount == 0) { + if (isRoot ()) { + // Root buffer, it must be ensured that no scaled buffers are left. + // See also FltkImgbuf::detachScaledBuf(). + if (scaledBuffers->isEmpty () && deleteOnUnref) { + delete this; + } else { + _MSG("FltkImgbuf[root %p]: not deleted. numScaled=%d\n", + this, scaledBuffers->size ()); + } + } else + // Scaled buffer buffer, simply delete it. + delete this; + } +} + +bool FltkImgbuf::lastReference () +{ + return refCount == 1 && + (scaledBuffers == NULL || scaledBuffers->isEmpty ()); +} + +void FltkImgbuf::setDeleteOnUnref (bool deleteOnUnref) +{ + assert (isRoot ()); + this->deleteOnUnref = deleteOnUnref; +} + +bool FltkImgbuf::isReferred () +{ + return refCount != 0 || + (scaledBuffers != NULL && !scaledBuffers->isEmpty ()); +} + + +int FltkImgbuf::scaledY(int ySrc) +{ + // TODO: May have to be adjusted. + assert (root != NULL); + return ySrc * height / root->height; +} + +int FltkImgbuf::backscaledY(int yScaled) +{ + assert (root != NULL); + + // Notice that rounding errors because of integers do not play a + // role. This method cannot be the exact inverse of scaledY, since + // scaleY is not bijective, and so not invertible. Instead, both + // values always return the smallest value. + return yScaled * root->height / height; +} + +void FltkImgbuf::draw (Fl_Widget *target, int xRoot, int yRoot, + int x, int y, int width, int height) +{ + // TODO: Clarify the question, whether "target" is the current widget + // (and so has not to be passed at all). + + _MSG("::draw: xRoot=%d x=%d yRoot=%d y=%d width=%d height=%d\n" + " this->width=%d this->height=%d\n", + xRoot, x, yRoot, y, width, height, this->width, this->height); + + if (x > this->width || y > this->height) { + return; + } + + if (x + width > this->width) { + width = this->width - x; + } + + if (y + height > this->height) { + height = this->height - y; + } + + fl_draw_image(rawdata+bpp*(y*this->width + x), xRoot + x, yRoot + y, width, + height, bpp, this->width * bpp); + +} + +} // namespace fltk +} // namespace dw diff --git a/dw/fltkimgbuf.hh b/dw/fltkimgbuf.hh new file mode 100644 index 0000000..0b8b554 --- /dev/null +++ b/dw/fltkimgbuf.hh @@ -0,0 +1,93 @@ +#ifndef __DW_FLTKIMGBUF_HH__ +#define __DW_FLTKIMGBUF_HH__ + +#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__ +# error Do not include this file directly, use "fltkcore.hh" instead. +#endif + +namespace dw { +namespace fltk { + +class FltkImgbuf: public core::Imgbuf +{ +private: + class GammaCorrectionTable: public lout::object::Object + { + public: + double gamma; + uchar map[256]; + }; + + FltkImgbuf *root; + int refCount; + bool deleteOnUnref; + lout::container::typed::List <FltkImgbuf> *scaledBuffers; + + int width, height; + Type type; + double gamma; + +//{ + int bpp; + uchar *rawdata; +//} + + // This is just for testing drawing, it has to be replaced by + // the image buffer. + lout::misc::BitSet *copiedRows; + + static lout::container::typed::Vector <GammaCorrectionTable> + *gammaCorrectionTables; + + static uchar *findGammaCorrectionTable (double gamma); + static bool excessiveImageDimensions (int width, int height); + + FltkImgbuf (Type type, int width, int height, double gamma, + FltkImgbuf *root); + void init (Type type, int width, int height, double gamma, FltkImgbuf *root); + int scaledY(int ySrc); + int backscaledY(int yScaled); + int isRoot() { return (root == NULL); } + void detachScaledBuf (FltkImgbuf *scaledBuf); + +protected: + ~FltkImgbuf (); + +public: + FltkImgbuf (Type type, int width, int height, double gamma); + + static void freeall (); + + void setCMap (int *colors, int num_colors); + inline void scaleRow (int row, const core::byte *data); + inline void scaleRowSimple (int row, const core::byte *data); + inline void scaleRowBeautiful (int row, const core::byte *data); + inline static void scaleBuffer (const core::byte *src, int srcWidth, + int srcHeight, core::byte *dest, + int destWidth, int destHeight, int bpp, + double gamma); + + void newScan (); + void copyRow (int row, const core::byte *data); + core::Imgbuf* getScaledBuf (int width, int height); + void getRowArea (int row, dw::core::Rectangle *area); + int getRootWidth (); + int getRootHeight (); + core::Imgbuf *createSimilarBuf (int width, int height); + void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc); + void ref (); + void unref (); + + bool lastReference (); + void setDeleteOnUnref (bool deleteOnUnref); + bool isReferred (); + + void draw (Fl_Widget *target, int xRoot, int yRoot, + int x, int y, int width, int height); +}; + +} // namespace fltk +} // namespace dw + +#endif // __DW_FLTK_IMGBUF_HH__ diff --git a/dw/fltkmisc.cc b/dw/fltkmisc.cc new file mode 100644 index 0000000..45230ad --- /dev/null +++ b/dw/fltkmisc.cc @@ -0,0 +1,58 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + +#include "../lout/msg.h" +#include "fltkmisc.hh" + +#include <FL/Fl.H> +#include <stdio.h> + +namespace dw { +namespace fltk { +namespace misc { + +int screenWidth () +{ + return Fl::w (); +} + +int screenHeight () +{ + return Fl::h (); +} + +void warpPointer (int x, int y) +{ + MSG_ERR("no warpPointer mechanism available.\n"); +} + +} // namespace misc +} // namespace fltk +} // namespace dw diff --git a/dw/fltkmisc.hh b/dw/fltkmisc.hh new file mode 100644 index 0000000..fc00431 --- /dev/null +++ b/dw/fltkmisc.hh @@ -0,0 +1,22 @@ +#ifndef __FLTKMISC_HH__ +#define __FLTKMISC_HH__ + +namespace dw { +namespace fltk { + +/** + * \brief Miscellaneous FLTK stuff. + */ +namespace misc { + +int screenWidth (); +int screenHeight (); + +void warpPointer (int x, int y); + +} // namespace misc +} // namespace fltk +} // namespace dw + + +#endif // __FLTKMISC_HH__ diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc new file mode 100644 index 0000000..a244765 --- /dev/null +++ b/dw/fltkplatform.cc @@ -0,0 +1,739 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include "../lout/msg.h" +#include "../lout/debug.hh" +#include "fltkcore.hh" + +#include <FL/fl_draw.H> +#include <FL/Fl_Box.H> +#include <FL/Fl_Tooltip.H> +#include <FL/Fl_Menu_Window.H> +#include <FL/Fl_Paged_Device.H> + +/* + * Local data + */ + +/* Tooltips */ +static Fl_Menu_Window *tt_window = NULL; +static int in_tooltip = 0, req_tooltip = 0; + +namespace dw { +namespace fltk { + +using namespace lout; + +/** + * \todo Distinction between italics and oblique would be nice. + */ + +container::typed::HashTable <dw::core::style::FontAttrs, + FltkFont> *FltkFont::fontsTable = + new container::typed::HashTable <dw::core::style::FontAttrs, + FltkFont> (false, false); + +container::typed::HashTable <lout::object::ConstString, + FltkFont::FontFamily> *FltkFont::systemFonts = + NULL; + +FltkFont::FontFamily FltkFont::standardFontFamily (FL_HELVETICA, + FL_HELVETICA_BOLD, + FL_HELVETICA_ITALIC, + FL_HELVETICA_BOLD_ITALIC); + +FltkFont::FontFamily::FontFamily (Fl_Font fontNormal, Fl_Font fontBold, + Fl_Font fontItalic, Fl_Font fontBoldItalic) +{ + font[0] = fontNormal; + font[1] = fontBold; + font[2] = fontItalic; + font[3] = fontBoldItalic; +} + +void FltkFont::FontFamily::set (Fl_Font f, int attrs) +{ + int idx = 0; + if (attrs & FL_BOLD) + idx += 1; + if (attrs & FL_ITALIC) + idx += 2; + font[idx] = f; +} + +Fl_Font FltkFont::FontFamily::get (int attrs) +{ + int idx = 0; + if (attrs & FL_BOLD) + idx += 1; + if (attrs & FL_ITALIC) + idx += 2; + + // should the desired font style not exist, we + // return the normal font of the fontFamily + return font[idx] >= 0 ? font[idx] : font[0]; +} + + + +FltkFont::FltkFont (core::style::FontAttrs *attrs) +{ + if (!systemFonts) + initSystemFonts (); + + copyAttrs (attrs); + + int fa = 0; + if (weight >= 500) + fa |= FL_BOLD; + if (style != core::style::FONT_STYLE_NORMAL) + fa |= FL_ITALIC; + + object::ConstString nameString (name); + FontFamily *family = systemFonts->get (&nameString); + if (!family) + family = &standardFontFamily; + + font = family->get (fa); + + fl_font(font, size); + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). + spaceWidth = misc::max(0, (int)fl_width(" ") + letterSpacing); + int xx, xy, xw, xh; + fl_text_extents("x", xx, xy, xw, xh); + xHeight = xh; + descent = fl_descent(); + ascent = fl_height() - descent; +} + +FltkFont::~FltkFont () +{ + fontsTable->remove (this); +} + +static void strstrip(char *big, const char *little) +{ + if (strlen(big) >= strlen(little) && + misc::AsciiStrcasecmp(big + strlen(big) - strlen(little), little) == 0) + *(big + strlen(big) - strlen(little)) = '\0'; +} + +void FltkFont::initSystemFonts () +{ + systemFonts = new container::typed::HashTable + <lout::object::ConstString, FontFamily> (true, true); + + int k = Fl::set_fonts ("-*-iso10646-1"); + for (int i = 0; i < k; i++) { + int t; + char *name = strdup (Fl::get_font_name ((Fl_Font) i, &t)); + + // normalize font family names (strip off "bold", "italic") + if (t & FL_ITALIC) + strstrip(name, " italic"); + if (t & FL_BOLD) + strstrip(name, " bold"); + + _MSG("Found font: %s%s%s\n", name, t & FL_BOLD ? " bold" : "", + t & FL_ITALIC ? " italic" : ""); + + object::String *familyName = new object::String(name); + free (name); + FontFamily *family = systemFonts->get (familyName); + + if (family) { + family->set ((Fl_Font) i, t); + delete familyName; + } else { + // set first font of family also as normal font in case there + // is no normal (non-bold, non-italic) font + family = new FontFamily ((Fl_Font) i, -1, -1, -1); + family->set ((Fl_Font) i, t); + systemFonts->put (familyName, family); + } + } +} + +bool +FltkFont::fontExists (const char *name) +{ + if (!systemFonts) + initSystemFonts (); + object::ConstString familyName (name); + return systemFonts->get (&familyName) != NULL; +} + +Fl_Font +FltkFont::get (const char *name, int attrs) +{ + if (!systemFonts) + initSystemFonts (); + object::ConstString familyName (name); + FontFamily *family = systemFonts->get (&familyName); + if (family) + return family->get (attrs); + else + return FL_HELVETICA; +} + +bool +FltkPlatform::fontExists (const char *name) +{ + return FltkFont::fontExists (name); +} + +FltkFont* +FltkFont::create (core::style::FontAttrs *attrs) +{ + FltkFont *font = fontsTable->get (attrs); + + if (font == NULL) { + font = new FltkFont (attrs); + fontsTable->put (font, font); + } + + return font; +} + +container::typed::HashTable <dw::core::style::ColorAttrs, + FltkColor> + *FltkColor::colorsTable = + new container::typed::HashTable <dw::core::style::ColorAttrs, + FltkColor> (false, false); + +FltkColor::FltkColor (int color): Color (color) +{ + this->color = color; + + if (!(colors[SHADING_NORMAL] = shadeColor (color, SHADING_NORMAL) << 8)) + colors[SHADING_NORMAL] = FL_BLACK; + if (!(colors[SHADING_INVERSE] = shadeColor (color, SHADING_INVERSE) << 8)) + colors[SHADING_INVERSE] = FL_BLACK; + if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8)) + colors[SHADING_DARK] = FL_BLACK; + if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8)) + colors[SHADING_LIGHT] = FL_BLACK; +} + +FltkColor::~FltkColor () +{ + colorsTable->remove (this); +} + +FltkColor * FltkColor::create (int col) +{ + ColorAttrs attrs(col); + FltkColor *color = colorsTable->get (&attrs); + + if (color == NULL) { + color = new FltkColor (col); + colorsTable->put (color, color); + } + + return color; +} + +FltkTooltip::FltkTooltip (const char *text) : Tooltip(text) +{ +} + +FltkTooltip::~FltkTooltip () +{ + if (in_tooltip || req_tooltip) + cancel(); /* cancel tooltip window */ +} + +FltkTooltip *FltkTooltip::create (const char *text) +{ + return new FltkTooltip(text); +} + +/* + * Tooltip callback: used to delay it a bit + * INVARIANT: Only one instance of this function is requested. + */ +static void tooltip_tcb(void *data) +{ + req_tooltip = 2; + ((FltkTooltip *)data)->onEnter(); + req_tooltip = 0; +} + +void FltkTooltip::onEnter() +{ + _MSG("FltkTooltip::onEnter\n"); + if (!str || !*str) + return; + if (req_tooltip == 0) { + Fl::remove_timeout(tooltip_tcb); + Fl::add_timeout(1.0, tooltip_tcb, this); + req_tooltip = 1; + return; + } + + if (!tt_window) { + tt_window = new Fl_Menu_Window(0,0,100,24); + tt_window->set_override(); + tt_window->box(FL_NO_BOX); + Fl_Box *b = new Fl_Box(0,0,100,24); + b->box(FL_BORDER_BOX); + b->color(fl_color_cube(FL_NUM_RED-1, FL_NUM_GREEN-1, FL_NUM_BLUE-2)); + b->labelcolor(FL_BLACK); + b->labelfont(FL_HELVETICA); + b->labelsize(14); + b->align(FL_ALIGN_WRAP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE); + tt_window->resizable(b); + tt_window->end(); + } + + /* prepare tooltip window */ + int x, y; + Fl_Box *box = (Fl_Box*)tt_window->child(0); + box->label(str); + Fl::get_mouse(x,y); y += 6; + /* calculate window size */ + int ww, hh; + ww = 800; // max width; + box->measure_label(ww, hh); + ww += 6 + 2 * Fl::box_dx(box->box()); + hh += 6 + 2 * Fl::box_dy(box->box()); + tt_window->resize(x,y,ww,hh); + tt_window->show(); + in_tooltip = 1; +} + +/* + * Leaving the widget cancels the tooltip + */ +void FltkTooltip::onLeave() +{ + _MSG(" FltkTooltip::onLeave in_tooltip=%d\n", in_tooltip); + cancel(); +} + +void FltkPlatform::cancelTooltip() +{ + FltkTooltip::cancel(); +} + +/* + * Remove a shown tooltip or cancel a pending one + */ +void FltkTooltip::cancel() +{ + if (req_tooltip) { + Fl::remove_timeout(tooltip_tcb); + req_tooltip = 0; + } + if (!in_tooltip) return; + in_tooltip = 0; + tt_window->hide(); + + /* WORKAROUND: (Black magic here) + * Hiding a tooltip with the keyboard or mousewheel doesn't work. + * The code below "fixes" the problem */ + Fl_Widget *widget = Fl::belowmouse(); + if (widget && widget->window()) { + widget->window()->damage(FL_DAMAGE_EXPOSE,0,0,1,1); + } +} + +void FltkTooltip::onMotion() +{ +} + +void FltkView::addFltkWidget (Fl_Widget *widget, + core::Allocation *allocation) +{ +} + +void FltkView::removeFltkWidget (Fl_Widget *widget) +{ +} + +void FltkView::allocateFltkWidget (Fl_Widget *widget, + core::Allocation *allocation) +{ +} + +void FltkView::drawFltkWidget (Fl_Widget *widget, core::Rectangle *area) +{ +} + + +core::ui::LabelButtonResource * +FltkPlatform::FltkResourceFactory::createLabelButtonResource (const char + *label) +{ + return new ui::FltkLabelButtonResource (platform, label); +} + +core::ui::ComplexButtonResource * +FltkPlatform::FltkResourceFactory::createComplexButtonResource (core::Widget + *widget, + bool relief) +{ + // Not needed within RTFL. + lout::misc::assertNotReached (); + return NULL; +} + +core::ui::ListResource * +FltkPlatform::FltkResourceFactory::createListResource (core::ui + ::ListResource + ::SelectionMode + selectionMode, int rows) +{ + return new ui::FltkListResource (platform, selectionMode, rows); +} + +core::ui::OptionMenuResource * +FltkPlatform::FltkResourceFactory::createOptionMenuResource () +{ + return new ui::FltkOptionMenuResource (platform); +} + +core::ui::EntryResource * +FltkPlatform::FltkResourceFactory::createEntryResource (int size, + bool password, + const char *label) +{ + return new ui::FltkEntryResource (platform, size, password, label); +} + +core::ui::MultiLineTextResource * +FltkPlatform::FltkResourceFactory::createMultiLineTextResource (int cols, + int rows) +{ + return new ui::FltkMultiLineTextResource (platform, cols, rows); +} + +core::ui::CheckButtonResource * +FltkPlatform::FltkResourceFactory::createCheckButtonResource (bool activated) +{ + return new ui::FltkCheckButtonResource (platform, activated); +} + +core::ui::RadioButtonResource +*FltkPlatform::FltkResourceFactory::createRadioButtonResource +(core::ui::RadioButtonResource *groupedWith, bool activated) +{ + return + new ui::FltkRadioButtonResource (platform, + (ui::FltkRadioButtonResource*) + groupedWith, + activated); +} + +// ---------------------------------------------------------------------- + +FltkPlatform::FltkPlatform () +{ + DBG_OBJ_CREATE ("dw::fltk::FltkPlatform"); + + layout = NULL; + idleQueue = new container::typed::List <IdleFunc> (true); + idleFuncRunning = false; + idleFuncId = 0; + + view = NULL; + resources = new container::typed::List <ui::FltkResource> (false); + + resourceFactory.setPlatform (this); +} + +FltkPlatform::~FltkPlatform () +{ + if (idleFuncRunning) + Fl::remove_idle (generalStaticIdle, (void*)this); + delete idleQueue; + delete resources; + + DBG_OBJ_DELETE (); +} + +void FltkPlatform::setLayout (core::Layout *layout) +{ + this->layout = layout; + DBG_OBJ_ASSOC_CHILD (layout); +} + + +void FltkPlatform::attachView (core::View *view) +{ + if (this->view) + MSG_ERR("FltkPlatform::attachView: multiple views!\n"); + this->view = (FltkView*)view; + + for (container::typed::Iterator <ui::FltkResource> it = + resources->iterator (); it.hasNext (); ) { + ui::FltkResource *resource = it.getNext (); + resource->attachView (this->view); + } +} + + +void FltkPlatform::detachView (core::View *view) +{ + if (this->view != view) + MSG_ERR("FltkPlatform::detachView: this->view: %p view: %p\n", + this->view, view); + + for (container::typed::Iterator <ui::FltkResource> it = + resources->iterator (); it.hasNext (); ) { + ui::FltkResource *resource = it.getNext (); + resource->detachView ((FltkView*)view); + } + this->view = NULL; +} + + +int FltkPlatform::textWidth (core::style::Font *font, const char *text, + int len) +{ + char chbuf[4]; + int c, cu; + int width = 0; + FltkFont *ff = (FltkFont*) font; + int curr = 0, next = 0, nb; + + if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) { + int sc_fontsize = lout::misc::roundInt(ff->size * 0.78); + for (curr = 0; next < len; curr = next) { + next = nextGlyph(text, curr); + c = fl_utf8decode(text + curr, text + next, &nb); + if ((cu = fl_toupper(c)) == c) { + /* already uppercase, just draw the character */ + fl_font(ff->font, ff->size); + if (fl_nonspacing(cu) == 0) { + width += font->letterSpacing; + width += (int)fl_width(text + curr, next - curr); + } + } else { + /* make utf8 string for converted char */ + nb = fl_utf8encode(cu, chbuf); + fl_font(ff->font, sc_fontsize); + if (fl_nonspacing(cu) == 0) { + width += font->letterSpacing; + width += (int)fl_width(chbuf, nb); + } + } + } + } else { + fl_font (ff->font, ff->size); + width = (int) fl_width (text, len); + + if (font->letterSpacing) { + int curr = 0, next = 0; + + while (next < len) { + next = nextGlyph(text, curr); + c = fl_utf8decode(text + curr, text + next, &nb); + if (fl_nonspacing(c) == 0) + width += font->letterSpacing; + curr = next; + } + } + } + + return width; +} + +char *FltkPlatform::textToUpper (const char *text, int len) +{ + char *newstr = NULL; + + if (len > 0) { + int newlen; + + newstr = (char*) malloc(3 * len + 1); + newlen = fl_utf_toupper((const unsigned char*)text, len, newstr); + assert(newlen <= 3 * len); + newstr[newlen] = '\0'; + } + return newstr; +} + +char *FltkPlatform::textToLower (const char *text, int len) +{ + char *newstr = NULL; + + if (len > 0) { + int newlen; + + newstr = (char*) malloc(3 * len + 1); + newlen = fl_utf_tolower((const unsigned char*)text, len, newstr); + assert(newlen <= 3 * len); + newstr[newlen] = '\0'; + } + return newstr; +} + +int FltkPlatform::nextGlyph (const char *text, int idx) +{ + return fl_utf8fwd (&text[idx + 1], text, &text[strlen (text)]) - text; +} + +int FltkPlatform::prevGlyph (const char *text, int idx) +{ + return fl_utf8back (&text[idx - 1], text, &text[strlen (text)]) - text; +} + +float FltkPlatform::dpiX () +{ + float horizontal, vertical; + + Fl::screen_dpi(horizontal, vertical); + return horizontal; +} + +float FltkPlatform::dpiY () +{ + float horizontal, vertical; + + Fl::screen_dpi(horizontal, vertical); + return vertical; +} + +void FltkPlatform::generalStaticIdle (void *data) +{ + ((FltkPlatform*)data)->generalIdle(); +} + +void FltkPlatform::generalIdle () +{ + IdleFunc *idleFunc; + + if (!idleQueue->isEmpty ()) { + /* Execute the first function in the list. */ + idleFunc = idleQueue->getFirst (); + (layout->*(idleFunc->func)) (); + + /* Remove this function. */ + idleQueue->removeRef(idleFunc); + } + + if (idleQueue->isEmpty()) { + idleFuncRunning = false; + Fl::remove_idle (generalStaticIdle, (void*)this); + } +} + +/** + * \todo Incomplete comments. + */ +int FltkPlatform::addIdle (void (core::Layout::*func) ()) +{ + /* + * Since ... (todo) we have to wrap around fltk_add_idle. There is only one + * idle function, the passed idle function is put into a queue. + */ + if (!idleFuncRunning) { + Fl::add_idle (generalStaticIdle, (void*)this); + idleFuncRunning = true; + } + + idleFuncId++; + + IdleFunc *idleFunc = new IdleFunc(); + idleFunc->id = idleFuncId; + idleFunc->func = func; + idleQueue->append (idleFunc); + + return idleFuncId; +} + +void FltkPlatform::removeIdle (int idleId) +{ + bool found; + container::typed::Iterator <IdleFunc> it; + IdleFunc *idleFunc; + + for (found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) { + idleFunc = it.getNext(); + if (idleFunc->id == idleId) { + idleQueue->removeRef (idleFunc); + found = true; + } + } + + if (idleFuncRunning && idleQueue->isEmpty()) + Fl::remove_idle (generalStaticIdle, (void*)this); +} + +core::style::Font *FltkPlatform::createFont (core::style::FontAttrs + *attrs, + bool tryEverything) +{ + return FltkFont::create (attrs); +} + +core::style::Color *FltkPlatform::createColor (int color) +{ + return FltkColor::create (color); +} + +core::style::Tooltip *FltkPlatform::createTooltip (const char *text) +{ + return FltkTooltip::create (text); +} + +void FltkPlatform::copySelection(const char *text) +{ + Fl::copy(text, strlen(text), 0); +} + +core::Imgbuf *FltkPlatform::createImgbuf (core::Imgbuf::Type type, + int width, int height, double gamma) +{ + return new FltkImgbuf (type, width, height, gamma); +} + +core::ui::ResourceFactory *FltkPlatform::getResourceFactory () +{ + return &resourceFactory; +} + + +void FltkPlatform::attachResource (ui::FltkResource *resource) +{ + resources->append (resource); + resource->attachView (view); +} + +void FltkPlatform::detachResource (ui::FltkResource *resource) +{ + resources->removeRef (resource); +} + +} // namespace fltk +} // namespace dw diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh new file mode 100644 index 0000000..2fb9563 --- /dev/null +++ b/dw/fltkplatform.hh @@ -0,0 +1,186 @@ +#ifndef __DW_FLTKPLATFORM_HH__ +#define __DW_FLTKPLATFORM_HH__ + +#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__ +# error Do not include this file directly, use "fltkcore.hh" instead. +#endif + +namespace dw { + +/** + * \brief This namespace contains FLTK implementations of Dw interfaces. + */ +namespace fltk { + +class FltkFont: public core::style::Font +{ + class FontFamily: public lout::object::Object { + Fl_Font font[4]; + public: + FontFamily (Fl_Font fontNormal, Fl_Font fontBold, + Fl_Font fontItalic, Fl_Font fontBoldItalic); + void set (Fl_Font, int attrs); + Fl_Font get (int attrs); + }; + + static FontFamily standardFontFamily; + + static lout::container::typed::HashTable <lout::object::ConstString, + FontFamily> *systemFonts; + static lout::container::typed::HashTable <dw::core::style::FontAttrs, + FltkFont> *fontsTable; + + FltkFont (core::style::FontAttrs *attrs); + ~FltkFont (); + + static void initSystemFonts (); + +public: + Fl_Font font; + + static FltkFont *create (core::style::FontAttrs *attrs); + static bool fontExists (const char *name); + static Fl_Font get (const char *name, int attrs); +}; + + +class FltkColor: public core::style::Color +{ + static lout::container::typed::HashTable <dw::core::style::ColorAttrs, + FltkColor> *colorsTable; + + FltkColor (int color); + ~FltkColor (); + +public: + int colors[SHADING_NUM]; + + static FltkColor *create(int color); +}; + +class FltkTooltip: public core::style::Tooltip +{ +private: + FltkTooltip (const char *text); + ~FltkTooltip (); +public: + static FltkTooltip *create(const char *text); + static void cancel(); + void onEnter(); + void onLeave(); + void onMotion(); +}; + + +/** + * \brief This interface adds some more methods for all flkt-based views. + */ +class FltkView: public core::View +{ +public: + virtual bool usesFltkWidgets () = 0; + + virtual void addFltkWidget (Fl_Widget *widget, + core::Allocation *allocation); + virtual void removeFltkWidget (Fl_Widget *widget); + virtual void allocateFltkWidget (Fl_Widget *widget, + core::Allocation *allocation); + virtual void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area); +}; + + +class FltkPlatform: public core::Platform +{ +private: + class FltkResourceFactory: public core::ui::ResourceFactory + { + private: + FltkPlatform *platform; + + public: + inline void setPlatform (FltkPlatform *platform) { + this->platform = platform; } + + core::ui::LabelButtonResource *createLabelButtonResource (const char + *label); + core::ui::ComplexButtonResource * + createComplexButtonResource (core::Widget *widget, bool relief); + core::ui::ListResource * + createListResource (core::ui::ListResource::SelectionMode selectionMode, + int rows); + core::ui::OptionMenuResource *createOptionMenuResource (); + core::ui::EntryResource *createEntryResource (int size, bool password, + const char *label); + core::ui::MultiLineTextResource *createMultiLineTextResource (int cols, + int rows); + core::ui::CheckButtonResource *createCheckButtonResource (bool + activated); + core::ui::RadioButtonResource * + createRadioButtonResource (core::ui::RadioButtonResource + *groupedWith, bool activated); + }; + + FltkResourceFactory resourceFactory; + + class IdleFunc: public lout::object::Object + { + public: + int id; + void (core::Layout::*func) (); + }; + + core::Layout *layout; + + lout::container::typed::List <IdleFunc> *idleQueue; + bool idleFuncRunning; + int idleFuncId; + + static void generalStaticIdle(void *data); + void generalIdle(); + + FltkView *view; + lout::container::typed::List <ui::FltkResource> *resources; + +public: + FltkPlatform (); + ~FltkPlatform (); + + void setLayout (core::Layout *layout); + + void attachView (core::View *view); + + void detachView (core::View *view); + + int textWidth (core::style::Font *font, const char *text, int len); + char *textToUpper (const char *text, int len); + char *textToLower (const char *text, int len); + int nextGlyph (const char *text, int idx); + int prevGlyph (const char *text, int idx); + float dpiX (); + float dpiY (); + + int addIdle (void (core::Layout::*func) ()); + void removeIdle (int idleId); + + core::style::Font *createFont (core::style::FontAttrs *attrs, + bool tryEverything); + bool fontExists (const char *name); + core::style::Color *createColor (int color); + core::style::Tooltip *createTooltip (const char *text); + void cancelTooltip(); + + core::Imgbuf *createImgbuf (core::Imgbuf::Type type, int width, int height, + double gamma); + + void copySelection(const char *text); + + core::ui::ResourceFactory *getResourceFactory (); + + void attachResource (ui::FltkResource *resource); + void detachResource (ui::FltkResource *resource); +}; + +} // namespace fltk +} // namespace dw + +#endif // __DW_FLTKPLATFORM_HH__ diff --git a/dw/fltkpreview.cc b/dw/fltkpreview.cc new file mode 100644 index 0000000..b234e81 --- /dev/null +++ b/dw/fltkpreview.cc @@ -0,0 +1,316 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "../lout/msg.h" + +#include "fltkpreview.hh" +#include "fltkmisc.hh" + +#include <FL/Fl.H> +#include <FL/Fl_Bitmap.H> +#include <FL/fl_draw.H> +#include <stdio.h> + +#include "preview.xbm" + +namespace dw { +namespace fltk { + +FltkPreview::FltkPreview (int x, int y, int w, int h, + dw::core::Layout *layout, const char *label): + FltkViewBase (x, y, w, h, label) +{ + layout->attachView (this); + + scrollX = 0; + scrollY = 0; + scrollWidth = 1; + scrollHeight = 1; +} + +FltkPreview::~FltkPreview () +{ +} + +int FltkPreview::handle (int event) +{ + return FltkViewBase::handle (event); +} + +int FltkPreview::translateViewXToCanvasX (int x) +{ + return x * canvasWidth / w (); +} + +int FltkPreview::translateViewYToCanvasY (int y) +{ + return y * canvasHeight / h (); +} + +int FltkPreview::translateCanvasXToViewX (int x) +{ + return x * w () / canvasWidth; +} + +int FltkPreview::translateCanvasYToViewY (int y) +{ + return y * h () / canvasHeight; +} + +void FltkPreview::setCanvasSize (int width, int ascent, int descent) +{ + FltkViewBase::setCanvasSize (width, ascent, descent); + if (parent() && parent()->visible ()) + ((FltkPreviewWindow*)parent())->reallocate (); +} + +bool FltkPreview::usesViewport () +{ + return true; +} + +int FltkPreview::getHScrollbarThickness () +{ + return 0; +} + +int FltkPreview::getVScrollbarThickness () +{ + return 0; +} + +void FltkPreview::scrollTo (int x, int y) +{ + scrollX = x; + scrollY = y; +} + +void FltkPreview::scroll (dw::core::ScrollCommand cmd) +{ + MSG_ERR("FltkPreview::scroll not implemented\n"); +} + +void FltkPreview::setViewportSize (int width, int height, + int hScrollbarThickness, + int vScrollbarThickness) +{ + scrollWidth = width - vScrollbarThickness; + scrollHeight = height - hScrollbarThickness; +} + +void FltkPreview::drawText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, const char *text, int len) +{ + /* + * We must call setfont() before calling getwidth() (or anything + * else that measures text). + */ + FltkFont *ff = (FltkFont*)font; + Fl::set_font(ff->font, translateCanvasXToViewX (ff->size)); +#if 0 + /** + * \todo Normally, this should already be known, maybe it + * should be passed? + */ + int width = (int)getwidth (text, len); + int height = font->ascent; // No descent, this would look to "bold". + + int x1 = translateCanvasXToViewX (x); + int y1 = translateCanvasYToViewY (y); + int x2 = translateCanvasXToViewX (x + width); + int y2 = translateCanvasYToViewY (y + height); + Rectangle rect (x1, y1, x2 - x1, y2 - y1); + + setcolor(((FltkColor*)color)->colors[shading]); + fillrect (rect); +#endif + fl_color(((FltkColor*)color)->colors[shading]); + fl_draw(text, len, translateCanvasXToViewX (x), translateCanvasYToViewY(y)); +} + +void FltkPreview::drawSimpleWrappedText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, int w, int h, + const char *text) +{ +} + +void FltkPreview::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, + int x, int y, int width, int height) +{ +} + +bool FltkPreview::usesFltkWidgets () +{ + return false; +} + +void FltkPreview::drawFltkWidget (Fl_Widget *widget, + core::Rectangle *area) +{ +} + +// ---------------------------------------------------------------------- + +FltkPreviewWindow::FltkPreviewWindow (dw::core::Layout *layout): + Fl_Menu_Window (1, 1) +{ + box (FL_EMBOSSED_BOX); + + begin (); + preview = new FltkPreview (BORDER_WIDTH, BORDER_WIDTH, 1, 1, layout); + end (); + + hide (); +} + +FltkPreviewWindow::~FltkPreviewWindow () +{ +} + +void FltkPreviewWindow::showWindow () +{ + reallocate (); + show (); +} + +void FltkPreviewWindow::reallocate () +{ + int maxWidth = misc::screenWidth () / 2; + int maxHeight = misc::screenHeight () * 4 / 5; + int mx, my, width, height; + bool warp = false; + + if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) { + // Expand to maximal height (most likely case). + width = preview->canvasWidth * maxHeight / preview->canvasHeight; + height = maxHeight; + } else { + // Expand to maximal width. + width = maxWidth; + height = preview->canvasHeight * maxWidth / preview->canvasWidth; + } + + Fl::get_mouse(mx, my); + + posX = mx - preview->translateCanvasXToViewX (preview->scrollX + + preview->scrollWidth / 2); + posY = my - preview->translateCanvasYToViewY (preview->scrollY + + preview->scrollHeight / 2); + + if (posX < 0) { + mx -= posX; + posX = 0; + warp = true; + } else if (posX + width > misc::screenWidth ()) { + mx -= (posX - (misc::screenWidth () - width)); + posX = misc::screenWidth () - width; + warp = true; + } + + if (posY < 0) { + my -= posY; + posY = 0; + warp = true; + } else if (posY + height > misc::screenHeight ()) { + my -= (posY - (misc::screenHeight () - height)); + posY = misc::screenHeight () - height; + warp = true; + } + + if (warp) + misc::warpPointer (mx, my); + + resize (posX, posY, width, height); + + preview->size(w () - 2 * BORDER_WIDTH, h () - 2 * BORDER_WIDTH); +} + +void FltkPreviewWindow::hideWindow () +{ + Fl_Window::hide (); +} + +void FltkPreviewWindow::scrollTo (int mouseX, int mouseY) +{ + preview->scrollX = + preview->translateViewXToCanvasX (mouseX - posX - BORDER_WIDTH) + - preview->scrollWidth / 2; + preview->scrollY = + preview->translateViewYToCanvasY (mouseY - posY - BORDER_WIDTH) + - preview->scrollHeight / 2; + preview->theLayout->scrollPosChanged (preview, + preview->scrollX, preview->scrollY); +} + +// ---------------------------------------------------------------------- + +FltkPreviewButton::FltkPreviewButton (int x, int y, int w, int h, + dw::core::Layout *layout, + const char *label): + Fl_Button (x, y, w, h, label) +{ + image (new Fl_Bitmap (preview_bits, preview_width, preview_height)); + window = new FltkPreviewWindow (layout); +} + +FltkPreviewButton::~FltkPreviewButton () +{ +} + +int FltkPreviewButton::handle (int event) +{ + /** \bug Some parts are missing. */ + + switch (event) { + case FL_PUSH: + window->showWindow (); + return Fl_Button::handle (event); + + case FL_DRAG: + if (window->visible ()) { + window->scrollTo (Fl::event_x_root (), Fl::event_y_root ()); + return 1; + } + return Fl_Button::handle (event); + + case FL_RELEASE: + window->hideWindow (); + return Fl_Button::handle (event); + + default: + return Fl_Button::handle (event); + } +} + +} // namespace fltk +} // namespace dw diff --git a/dw/fltkpreview.hh b/dw/fltkpreview.hh new file mode 100644 index 0000000..2382b86 --- /dev/null +++ b/dw/fltkpreview.hh @@ -0,0 +1,95 @@ +#ifndef __FlTKPREVIEW_HH__ +#define __FlTKPREVIEW_HH__ + +#include <FL/Fl_Button.H> +#include <FL/Fl_Menu_Window.H> +#include "fltkviewbase.hh" + +namespace dw { +namespace fltk { + +class FltkPreview: public FltkViewBase +{ + friend class FltkPreviewWindow; + +private: + int scrollX, scrollY, scrollWidth, scrollHeight; + +protected: + int translateViewXToCanvasX (int x); + int translateViewYToCanvasY (int y); + int translateCanvasXToViewX (int x); + int translateCanvasYToViewY (int y); + +public: + FltkPreview (int x, int y, int w, int h, dw::core::Layout *layout, + const char *label = 0); + ~FltkPreview (); + + int handle (int event); + + void setCanvasSize (int width, int ascent, int descent); + + bool usesViewport (); + int getHScrollbarThickness (); + int getVScrollbarThickness (); + void scrollTo (int x, int y); + void scroll (dw::core::ScrollCommand cmd); + void setViewportSize (int width, int height, + int hScrollbarThickness, int vScrollbarThickness); + + void drawText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, const char *text, int len); + void drawSimpleWrappedText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, int w, int h, + const char *text); + void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, + int x, int y, int width, int height); + + bool usesFltkWidgets (); + void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area); +}; + + +class FltkPreviewWindow: public Fl_Menu_Window +{ +private: + enum { BORDER_WIDTH = 2 }; + + FltkPreview *preview; + int posX, posY; + +public: + FltkPreviewWindow (dw::core::Layout *layout); + ~FltkPreviewWindow (); + + void reallocate (); + + void showWindow (); + void hideWindow (); + + void scrollTo (int mouseX, int mouseY); +}; + + +class FltkPreviewButton: public Fl_Button +{ +private: + FltkPreviewWindow *window; + +public: + FltkPreviewButton (int x, int y, int w, int h, + dw::core::Layout *layout, const char *label = 0); + ~FltkPreviewButton (); + + int handle (int event); +}; + +} // namespace fltk +} // namespace dw + +#endif // __FlTKPREVIEW_HH__ diff --git a/dw/fltkui.cc b/dw/fltkui.cc new file mode 100644 index 0000000..ce47dcd --- /dev/null +++ b/dw/fltkui.cc @@ -0,0 +1,1395 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "fltkcore.hh" +#include "../lout/msg.h" +#include "../lout/misc.hh" + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Input.H> +#include <FL/Fl_Text_Editor.H> +#include <FL/Fl_Check_Button.H> +#include <FL/Fl_Round_Button.H> +#include <FL/Fl_Choice.H> +#include <FL/Fl_Browser.H> + +#include <stdio.h> + +//---------------------------------------------------------------------------- +/* + * Local sub classes + */ + +/* + * Used to enable CTRL+{a,e,d,k} in form inputs (for start,end,del,cut) + */ +class CustInput2 : public Fl_Input { +public: + CustInput2 (int x, int y, int w, int h, const char* l=0) : + Fl_Input(x,y,w,h,l) {}; + int handle(int e); +}; + +int CustInput2::handle(int e) +{ + int k = Fl::event_key(); + + _MSG("CustInput2::handle event=%d\n", e); + + // We're only interested in some flags + unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT); + + if (e == FL_KEYBOARD) { + if (k == FL_Page_Down || k == FL_Page_Up || k == FL_Up || k == FL_Down) { + // Let them through for key commands and viewport motion. + return 0; + } + if (modifier == FL_CTRL) { + if (k == 'a' || k == 'e') { + position(k == 'a' ? 0 : size()); + return 1; + } else if (k == 'k') { + cut(position(), size()); + return 1; + } else if (k == 'd') { + cut(position(), position()+1); + return 1; + } else if (k == 'h' || k == 'i' || k == 'j' || k == 'l' || k == 'm') { + // Fl_Input wants to use ^H as backspace, and also "insert a few + // selected control characters literally", but this gets in the way + // of key commands. + return 0; + } + } + } + return Fl_Input::handle(e); +} + + +/* + * Used to handle some keystrokes as shortcuts to option menuitems + * (i.e. jump to the next menuitem whose label starts with the pressed key) + */ +class CustChoice : public Fl_Choice { +public: + CustChoice (int x, int y, int w, int h, const char* l=0) : + Fl_Choice(x,y,w,h,l) {}; + int handle(int e); +}; + +int CustChoice::handle(int e) +{ + int k = Fl::event_key(); + unsigned modifier = Fl::event_state() & (FL_SHIFT|FL_CTRL|FL_ALT|FL_META); + + _MSG("CustChoice::handle %p e=%d active=%d focus=%d\n", + this, e, active(), (Fl::focus() == this)); + if (Fl::focus() != this) { + ; // Not Focused, let FLTK handle it + } else if (e == FL_KEYDOWN && modifier == 0) { + if (k == FL_Enter || k == FL_Down) { + return Fl_Choice::handle(FL_PUSH); // activate menu + + } else if (isalnum(k)) { // try key as shortcut to menuitem + int t = value()+1 >= size() ? 0 : value()+1; + while (t != value()) { + const Fl_Menu_Item *mi = &(menu()[t]); + if (mi->submenu()) // submenu? + ; + else if (mi->label() && mi->active()) { // menu item? + if (k == tolower(mi->label()[0])) { + value(mi); + return 1; // Let FLTK know we used this key + } + } + if (++t == size()) + t = 0; + } + } + } + + return Fl_Choice::handle(e); +} + +//---------------------------------------------------------------------------- + +namespace dw { +namespace fltk { +namespace ui { + +enum { RELIEF_X_THICKNESS = 3, RELIEF_Y_THICKNESS = 3 }; + +using namespace lout::object; +using namespace lout::container::typed; + +FltkResource::FltkResource (FltkPlatform *platform) +{ + DBG_OBJ_CREATE ("dw::fltk::ui::FltkResource"); + + this->platform = platform; + + allocation.x = 0; + allocation.y = 0; + allocation.width = 1; + allocation.ascent = 1; + allocation.descent = 0; + + style = NULL; + + enabled = true; +} + +/** + * This is not a constructor, since it calls some virtual methods, which + * should not be done in a C++ base constructor. + */ +void FltkResource::init (FltkPlatform *platform) +{ + view = NULL; + widget = NULL; + platform->attachResource (this); +} + +FltkResource::~FltkResource () +{ + platform->detachResource (this); + if (widget) { + if (view) { + view->removeFltkWidget(widget); + } + delete widget; + } + if (style) + style->unref (); + + DBG_OBJ_DELETE (); +} + +void FltkResource::attachView (FltkView *view) +{ + if (this->view) + MSG_ERR("FltkResource::attachView: multiple views!\n"); + + if (view->usesFltkWidgets ()) { + this->view = view; + + widget = createNewWidget (&allocation); + view->addFltkWidget (widget, &allocation); + if (style) + setWidgetStyle (widget, style); + if (! enabled) + widget->deactivate (); + } +} + +void FltkResource::detachView (FltkView *view) +{ + if (this->view != view) + MSG_ERR("FltkResource::detachView: this->view: %p view: %p\n", + this->view, view); + this->view = NULL; +} + +void FltkResource::sizeAllocate (core::Allocation *allocation) +{ + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + this->allocation = *allocation; + view->allocateFltkWidget (widget, allocation); + + DBG_OBJ_LEAVE (); +} + +void FltkResource::draw (core::View *view, core::Rectangle *area) +{ + FltkView *fltkView = (FltkView*)view; + if (fltkView->usesFltkWidgets () && this->view == fltkView) { + fltkView->drawFltkWidget (widget, area); + } +} + +void FltkResource::setStyle (core::style::Style *style) +{ + if (this->style) + this->style->unref (); + + this->style = style; + style->ref (); + + setWidgetStyle (widget, style); +} + +void FltkResource::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + FltkFont *font = (FltkFont*)style->font; + widget->labelsize (font->size); + widget->labelfont (font->font); + + FltkColor *bg = (FltkColor*)style->backgroundColor; + if (bg) { + int normal_bg = bg->colors[FltkColor::SHADING_NORMAL]; + + if (style->color) { + int style_fg = ((FltkColor*)style->color)->colors + [FltkColor::SHADING_NORMAL]; + Fl_Color fg = fl_contrast(style_fg, normal_bg); + + widget->labelcolor(fg); + widget->selection_color(fg); + } + + widget->color(normal_bg); + } +} + +void FltkResource::setDisplayed(bool displayed) +{ + if (displayed) + widget->show(); + else + widget->hide(); +} + +bool FltkResource::displayed() +{ + bool ret = false; + + if (widget) { + // visible() is not the same thing as being show()n exactly, but + // show()/hide() set it appropriately for our purposes. + ret = widget->visible(); + } + return ret; +} + +bool FltkResource::isEnabled () +{ + return enabled; +} + +void FltkResource::setEnabled (bool enabled) +{ + this->enabled = enabled; + + if (enabled) + widget->activate (); + else + widget->deactivate (); +} + +// ---------------------------------------------------------------------- + +template <class I> FltkSpecificResource<I>::FltkSpecificResource (FltkPlatform + *platform) : + FltkResource (platform) +{ + DBG_OBJ_CREATE ("dw::fltk::ui::FltkSpecificResource<>"); + DBG_OBJ_BASECLASS (I); + DBG_OBJ_BASECLASS (FltkResource); +} + +template <class I> FltkSpecificResource<I>::~FltkSpecificResource () +{ + DBG_OBJ_DELETE (); +} + +template <class I> void FltkSpecificResource<I>::sizeAllocate (core::Allocation + *allocation) +{ + FltkResource::sizeAllocate (allocation); +} + +template <class I> void FltkSpecificResource<I>::draw (core::View *view, + core::Rectangle *area) +{ + FltkResource::draw (view, area); +} + +template <class I> void FltkSpecificResource<I>::setStyle (core::style::Style + *style) +{ + FltkResource::setStyle (style); +} + +template <class I> bool FltkSpecificResource<I>::isEnabled () +{ + return FltkResource::isEnabled (); +} + +template <class I> void FltkSpecificResource<I>::setEnabled (bool enabled) +{ + FltkResource::setEnabled (enabled); +} + +// ---------------------------------------------------------------------- + +class EnterButton : public Fl_Button { +public: + EnterButton (int x,int y,int w,int h, const char* label = 0) : + Fl_Button (x,y,w,h,label) {}; + int handle(int e); +}; + +int EnterButton::handle(int e) +{ + if (e == FL_KEYBOARD && Fl::focus() == this && Fl::event_key() == FL_Enter){ + set_changed(); + simulate_key_action(); + do_callback(); + return 1; + } + return Fl_Button::handle(e); +} + +FltkLabelButtonResource::FltkLabelButtonResource (FltkPlatform *platform, + const char *label): + FltkSpecificResource <dw::core::ui::LabelButtonResource> (platform) +{ + this->label = strdup (label); + init (platform); +} + +FltkLabelButtonResource::~FltkLabelButtonResource () +{ + free((char *)label); +} + +Fl_Widget *FltkLabelButtonResource::createNewWidget (core::Allocation + *allocation) +{ + Fl_Button *button = + new EnterButton (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent, label); + button->callback (widgetCallback, this); + button->when (FL_WHEN_RELEASE); + return button; +} + +void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + if (style) { + FltkFont *font = (FltkFont*)style->font; + fl_font(font->font,font->size); + requisition->width = + (int)fl_width (label, strlen (label)) + + 2 * RELIEF_X_THICKNESS; + requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; + requisition->descent = font->descent + RELIEF_Y_THICKNESS; + } else { + requisition->width = 1; + requisition->ascent = 1; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +/* + * Get FLTK state and translate to dw + * + * TODO: find a good home for this and the fltkviewbase.cc original. + */ +static core::ButtonState getDwButtonState () +{ + int s1 = Fl::event_state (); + int s2 = (core::ButtonState)0; + + if (s1 & FL_SHIFT) s2 |= core::SHIFT_MASK; + if (s1 & FL_CTRL) s2 |= core::CONTROL_MASK; + if (s1 & FL_ALT) s2 |= core::META_MASK; + if (s1 & FL_BUTTON1) s2 |= core::BUTTON1_MASK; + if (s1 & FL_BUTTON2) s2 |= core::BUTTON2_MASK; + if (s1 & FL_BUTTON3) s2 |= core::BUTTON3_MASK; + + return (core::ButtonState)s2; +} + +static void setButtonEvent(dw::core::EventButton *event) +{ + event->xCanvas = Fl::event_x(); + event->yCanvas = Fl::event_y(); + event->state = getDwButtonState(); + event->button = Fl::event_button(); + event->numPressed = Fl::event_clicks() + 1; +} + +void FltkLabelButtonResource::widgetCallback (Fl_Widget *widget, + void *data) +{ + if (!Fl::event_button3()) { + FltkLabelButtonResource *lbr = (FltkLabelButtonResource*) data; + dw::core::EventButton event; + setButtonEvent(&event); + lbr->emitClicked(&event); + } +} + +const char *FltkLabelButtonResource::getLabel () +{ + return label; +} + + +void FltkLabelButtonResource::setLabel (const char *label) +{ + free((char *)this->label); + this->label = strdup (label); + + widget->label (this->label); + queueResize (true); +} + +// ---------------------------------------------------------------------- + +FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int size, + bool password, const char *label): + FltkSpecificResource <dw::core::ui::EntryResource> (platform) +{ + this->size = size; + this->password = password; + this->label = label ? strdup(label) : NULL; + this->label_w = 0; + + initText = NULL; + editable = false; + + init (platform); +} + +FltkEntryResource::~FltkEntryResource () +{ + if (initText) + free((char *)initText); + if (label) + free(label); +} + +Fl_Widget *FltkEntryResource::createNewWidget (core::Allocation + *allocation) +{ + Fl_Input *input = + new CustInput2(allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + if (password) + input->type(FL_SECRET_INPUT); + input->callback (widgetCallback, this); + input->when (FL_WHEN_ENTER_KEY_ALWAYS); + + if (label) { + input->label(label); + input->align(FL_ALIGN_LEFT); + } + if (initText) + input->value (initText); + + return input; +} + +void FltkEntryResource::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + Fl_Input *in = (Fl_Input *)widget; + + FltkResource::setWidgetStyle(widget, style); + + in->textcolor(widget->labelcolor()); + in->cursor_color(in->textcolor()); + in->textsize(in->labelsize()); + in->textfont(in->labelfont()); + + if (label) { + int h; + label_w = 0; + widget->measure_label(label_w, h); + label_w += RELIEF_X_THICKNESS; + } +} + +void FltkEntryResource::setDisplayed(bool displayed) +{ + FltkResource::setDisplayed(displayed); + queueResize(true); +} + +void FltkEntryResource::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + if (displayed() && style) { + FltkFont *font = (FltkFont*)style->font; + fl_font(font->font,font->size); + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). + requisition->width = + (int)fl_width ("n") + * (size == UNLIMITED_SIZE ? 10 : size) + + label_w + (2 * RELIEF_X_THICKNESS); + requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; + requisition->descent = font->descent + RELIEF_Y_THICKNESS; + } else { + requisition->width = 0; + requisition->ascent = 0; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +void FltkEntryResource::sizeAllocate (core::Allocation *allocation) +{ + if (!label) { + FltkResource::sizeAllocate(allocation); + } else { + DBG_OBJ_MSGF ("resize", 0, + "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + this->allocation = *allocation; + + /* push the Fl_Input over to the right of the label */ + core::Allocation a = this->allocation; + a.x += this->label_w; + a.width -= this->label_w; + view->allocateFltkWidget (widget, &a); + } +} + +void FltkEntryResource::widgetCallback (Fl_Widget *widget, void *data) +{ + ((FltkEntryResource*)data)->emitActivate (); +} + +const char *FltkEntryResource::getText () +{ + return ((Fl_Input*)widget)->value (); +} + +void FltkEntryResource::setText (const char *text) +{ + if (initText) + free((char *)initText); + initText = strdup (text); + + ((Fl_Input*)widget)->value (initText); +} + +bool FltkEntryResource::isEditable () +{ + return editable; +} + +void FltkEntryResource::setEditable (bool editable) +{ + this->editable = editable; +} + +void FltkEntryResource::setMaxLength (int maxlen) +{ + ((Fl_Input *)widget)->maximum_size(maxlen); +} + +// ---------------------------------------------------------------------- + +static int kf_backspace_word (int c, Fl_Text_Editor *e) +{ + int p1, p2 = e->insert_position(); + + e->previous_word(); + p1 = e->insert_position(); + e->buffer()->remove(p1, p2); + e->show_insert_position(); + e->set_changed(); + if (e->when() & FL_WHEN_CHANGED) + e->do_callback(); + return 0; +} + +FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform, + int cols, int rows): + FltkSpecificResource <dw::core::ui::MultiLineTextResource> (platform) +{ + buffer = new Fl_Text_Buffer; + text_copy = NULL; + editable = false; + + numCols = cols; + numRows = rows; + + // Check values. Upper bound check is left to the caller. + if (numCols < 1) { + MSG_WARN("numCols = %d is set to 1.\n", numCols); + numCols = 1; + } + if (numRows < 1) { + MSG_WARN("numRows = %d is set to 1.\n", numRows); + numRows = 1; + } + + init (platform); +} + +FltkMultiLineTextResource::~FltkMultiLineTextResource () +{ + /* Free memory avoiding a double-free of text buffers */ + ((Fl_Text_Editor *) widget)->buffer (0); + delete buffer; + if (text_copy) + free(text_copy); +} + +Fl_Widget *FltkMultiLineTextResource::createNewWidget (core::Allocation + *allocation) +{ + Fl_Text_Editor *text = + new Fl_Text_Editor (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + text->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0); + text->buffer (buffer); + text->remove_key_binding(FL_BackSpace, FL_TEXT_EDITOR_ANY_STATE); + text->add_key_binding(FL_BackSpace, 0, Fl_Text_Editor::kf_backspace); + text->add_key_binding(FL_BackSpace, FL_CTRL, kf_backspace_word); + return text; +} + +void FltkMultiLineTextResource::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + Fl_Text_Editor *ed = (Fl_Text_Editor *)widget; + + FltkResource::setWidgetStyle(widget, style); + + ed->textcolor(widget->labelcolor()); + ed->cursor_color(ed->textcolor()); + ed->textsize(ed->labelsize()); + ed->textfont(ed->labelfont()); +} + +void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + if (style) { + FltkFont *font = (FltkFont*)style->font; + fl_font(font->font,font->size); + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). + requisition->width = + (int)fl_width ("n") * numCols + 2 * RELIEF_X_THICKNESS; + requisition->ascent = + RELIEF_Y_THICKNESS + font->ascent + + (font->ascent + font->descent) * (numRows - 1); + requisition->descent = + font->descent + + RELIEF_Y_THICKNESS; + } else { + requisition->width = 1; + requisition->ascent = 1; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +const char *FltkMultiLineTextResource::getText () +{ + /* FLTK-1.3 insists upon returning a new copy of the buffer text, so + * we have to keep track of it. + */ + if (text_copy) + free(text_copy); + text_copy = buffer->text(); + return text_copy; +} + +void FltkMultiLineTextResource::setText (const char *text) +{ + buffer->text (text); +} + +bool FltkMultiLineTextResource::isEditable () +{ + return editable; +} + +void FltkMultiLineTextResource::setEditable (bool editable) +{ + this->editable = editable; +} + +// ---------------------------------------------------------------------- + +template <class I> +FltkToggleButtonResource<I>::FltkToggleButtonResource (FltkPlatform *platform, + bool activated): + FltkSpecificResource <I> (platform) +{ + initActivated = activated; +} + + +template <class I> +FltkToggleButtonResource<I>::~FltkToggleButtonResource () +{ +} + + +template <class I> +Fl_Widget *FltkToggleButtonResource<I>::createNewWidget (core::Allocation + *allocation) +{ + Fl_Button *button = createNewButton (allocation); + button->value (initActivated); + return button; +} + +template <class I> +void FltkToggleButtonResource<I>::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + FltkResource::setWidgetStyle(widget, style); + + widget->selection_color(FL_BLACK); +} + + +template <class I> +void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + FltkFont *font = (FltkFont *) + (this->FltkResource::style ? this->FltkResource::style->font : NULL); + + if (font) { + fl_font(font->font, font->size); + requisition->width = font->ascent + font->descent + 2*RELIEF_X_THICKNESS; + requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; + requisition->descent = font->descent + RELIEF_Y_THICKNESS; + } else { + requisition->width = 1; + requisition->ascent = 1; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + + +template <class I> +bool FltkToggleButtonResource<I>::isActivated () +{ + return ((Fl_Button*)this->widget)->value (); +} + + +template <class I> +void FltkToggleButtonResource<I>::setActivated (bool activated) +{ + initActivated = activated; + ((Fl_Button*)this->widget)->value (initActivated); +} + +// ---------------------------------------------------------------------- + +FltkCheckButtonResource::FltkCheckButtonResource (FltkPlatform *platform, + bool activated): + FltkToggleButtonResource<dw::core::ui::CheckButtonResource> (platform, + activated) +{ + init (platform); +} + + +FltkCheckButtonResource::~FltkCheckButtonResource () +{ +} + + +Fl_Button *FltkCheckButtonResource::createNewButton (core::Allocation + *allocation) +{ + Fl_Check_Button *cb = + new Fl_Check_Button (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + return cb; +} + +// ---------------------------------------------------------------------- + +bool FltkRadioButtonResource::Group::FltkGroupIterator::hasNext () +{ + return it.hasNext (); +} + +dw::core::ui::RadioButtonResource +*FltkRadioButtonResource::Group::FltkGroupIterator::getNext () +{ + return (dw::core::ui::RadioButtonResource*)it.getNext (); +} + +void FltkRadioButtonResource::Group::FltkGroupIterator::unref () +{ + delete this; +} + + +FltkRadioButtonResource::Group::Group (FltkRadioButtonResource + *radioButtonResource) +{ + list = new lout::container::typed::List <FltkRadioButtonResource> (false); + connect (radioButtonResource); +} + +FltkRadioButtonResource::Group::~Group () +{ + delete list; +} + +void FltkRadioButtonResource::Group::connect (FltkRadioButtonResource + *radioButtonResource) +{ + list->append (radioButtonResource); +} + +void FltkRadioButtonResource::Group::unconnect (FltkRadioButtonResource + *radioButtonResource) +{ + list->removeRef (radioButtonResource); + if (list->isEmpty ()) + delete this; +} + + +FltkRadioButtonResource::FltkRadioButtonResource (FltkPlatform *platform, + FltkRadioButtonResource + *groupedWith, + bool activated): + FltkToggleButtonResource<dw::core::ui::RadioButtonResource> (platform, + activated) +{ + init (platform); + + if (groupedWith) { + group = groupedWith->group; + group->connect (this); + } else + group = new Group (this); +} + + +FltkRadioButtonResource::~FltkRadioButtonResource () +{ + group->unconnect (this); +} + +dw::core::ui::RadioButtonResource::GroupIterator +*FltkRadioButtonResource::groupIterator () +{ + return group->groupIterator (); +} + +void FltkRadioButtonResource::widgetCallback (Fl_Widget *widget, + void *data) +{ + if (widget->when () & FL_WHEN_CHANGED) + ((FltkRadioButtonResource*)data)->buttonClicked (); +} + +void FltkRadioButtonResource::buttonClicked () +{ + for (Iterator <FltkRadioButtonResource> it = group->iterator (); + it.hasNext (); ) { + FltkRadioButtonResource *other = it.getNext (); + other->setActivated (other == this); + } +} + +Fl_Button *FltkRadioButtonResource::createNewButton (core::Allocation + *allocation) +{ + /* + * Groups of Fl_Radio_Button must be added to one Fl_Group, which is + * not possible in this context. For this, we do the grouping ourself, + * based on FltkRadioButtonResource::Group. + * + * What we actually need for this, is a widget, which behaves like a + * check button, but looks like a radio button. The first depends on the + * type, the second on the style. Since the type is simpler to change + * than the style, we create a radio button, and then change the type + * (instead of creating a check button, and changing the style). + */ + + Fl_Button *button = + new Fl_Round_Button (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + button->when (FL_WHEN_CHANGED); + button->callback (widgetCallback, this); + button->type (FL_TOGGLE_BUTTON); + + return button; +} + +// ---------------------------------------------------------------------- + +template <class I> dw::core::Iterator * +FltkSelectionResource<I>::iterator (dw::core::Content::Type mask, bool atEnd) +{ + /** \bug Implementation. */ + return new core::EmptyIterator (this->getEmbed (), mask, atEnd); +} + +// ---------------------------------------------------------------------- + +FltkOptionMenuResource::FltkOptionMenuResource (FltkPlatform *platform): + FltkSelectionResource <dw::core::ui::OptionMenuResource> (platform) +{ + /* Fl_Menu_ does not like multiple menu items with the same label, and + * insert() treats some characters specially unless escaped, so let's + * do our own menu handling. + */ + itemsAllocated = 0x10; + menu = new Fl_Menu_Item[itemsAllocated]; + memset(menu, 0, itemsAllocated * sizeof(Fl_Menu_Item)); + itemsUsed = 1; // menu[0].text == NULL, which is an end-of-menu marker. + + init (platform); +} + +FltkOptionMenuResource::~FltkOptionMenuResource () +{ + for (int i = 0; i < itemsUsed; i++) { + if (menu[i].text) + free((char *) menu[i].text); + } + delete[] menu; +} + +void FltkOptionMenuResource::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + Fl_Choice *ch = (Fl_Choice *)widget; + + FltkResource::setWidgetStyle(widget, style); + + ch->textcolor(widget->labelcolor()); + ch->textfont(ch->labelfont()); + ch->textsize(ch->labelsize()); +} + +Fl_Widget *FltkOptionMenuResource::createNewWidget (core::Allocation + *allocation) +{ + Fl_Choice *choice = + new CustChoice (allocation->x, allocation->y, + allocation->width, + allocation->ascent + allocation->descent); + choice->menu(menu); + return choice; +} + +void FltkOptionMenuResource::widgetCallback (Fl_Widget *widget, + void *data) +{ +} + +int FltkOptionMenuResource::getMaxItemWidth() +{ + int i, max = 0; + + for (i = 0; i < itemsUsed; i++) { + int width = 0; + const char *str = menu[i].text; + + if (str) { + width = fl_width(str); + if (width > max) + max = width; + } + } + return max; +} + +void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + if (style) { + FltkFont *font = (FltkFont*)style->font; + fl_font(font->font, font->size); + int maxItemWidth = getMaxItemWidth (); + requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; + requisition->descent = font->descent + RELIEF_Y_THICKNESS; + requisition->width = maxItemWidth + + (requisition->ascent + requisition->descent) + + 2 * RELIEF_X_THICKNESS; + } else { + requisition->width = 1; + requisition->ascent = 1; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +void FltkOptionMenuResource::enlargeMenu () +{ + Fl_Choice *ch = (Fl_Choice *)widget; + int selected = ch->value(); + Fl_Menu_Item *newMenu; + + itemsAllocated += 0x10; + newMenu = new Fl_Menu_Item[itemsAllocated]; + memcpy(newMenu, menu, itemsUsed * sizeof(Fl_Menu_Item)); + memset(newMenu + itemsUsed, 0, 0x10 * sizeof(Fl_Menu_Item)); + delete[] menu; + menu = newMenu; + ch->menu(menu); + ch->value(selected); +} + +Fl_Menu_Item *FltkOptionMenuResource::newItem() +{ + Fl_Menu_Item *item; + + if (itemsUsed == itemsAllocated) + enlargeMenu(); + + item = menu + itemsUsed - 1; + itemsUsed++; + + return item; +} + +void FltkOptionMenuResource::addItem (const char *str, + bool enabled, bool selected) +{ + Fl_Menu_Item *item = newItem(); + + item->text = strdup(str); + + if (enabled == false) + item->flags = FL_MENU_INACTIVE; + + if (selected) + ((Fl_Choice *)widget)->value(item); + + queueResize (true); +} + +void FltkOptionMenuResource::setItem (int index, bool selected) +{ + if (selected) + ((Fl_Choice *)widget)->value(menu+index); +} + +void FltkOptionMenuResource::pushGroup (const char *name, bool enabled) +{ + Fl_Menu_Item *item = newItem(); + + item->text = strdup(name); + + if (enabled == false) + item->flags = FL_MENU_INACTIVE; + + item->flags |= FL_SUBMENU; + + queueResize (true); +} + +void FltkOptionMenuResource::popGroup () +{ + /* Item with NULL text field closes the submenu */ + newItem(); + queueResize (true); +} + +bool FltkOptionMenuResource::isSelected (int index) +{ + return index == ((Fl_Choice *)widget)->value(); +} + +int FltkOptionMenuResource::getNumberOfItems() +{ + return ((Fl_Choice*)widget)->size(); +} + +// ---------------------------------------------------------------------- + +class CustBrowser : public Fl_Browser { +public: + CustBrowser(int x, int y, int w, int h) : Fl_Browser(x, y, w, h) {}; + int full_width() const; + int full_height() const {return Fl_Browser::full_height();} + int avg_height() {return size() ? Fl_Browser_::incr_height() : 0;} +}; + +/* + * Fl_Browser_ has a full_width(), but it has a tendency to contain 0, so... + */ +int CustBrowser::full_width() const +{ + int max = 0; + void *item = item_first(); + + while (item) { + int w = item_width(item); + + if (w > max) + max = w; + + item = item_next(item); + } + return max; +} + +FltkListResource::FltkListResource (FltkPlatform *platform, + core::ui::ListResource::SelectionMode + selectionMode, int rowCount): + FltkSelectionResource <dw::core::ui::ListResource> (platform), + currDepth(0) +{ + mode = selectionMode; + showRows = rowCount; + init (platform); +} + +FltkListResource::~FltkListResource () +{ +} + + +Fl_Widget *FltkListResource::createNewWidget (core::Allocation *allocation) +{ + CustBrowser *b = + new CustBrowser (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + + b->type((mode == SELECTION_MULTIPLE) ? FL_MULTI_BROWSER : FL_HOLD_BROWSER); + b->callback(widgetCallback, this); + b->when(FL_WHEN_CHANGED); + b->column_widths(colWidths); + b->column_char('\a'); // I just chose a nonprinting character. + + return b; +} + +void FltkListResource::setWidgetStyle (Fl_Widget *widget, + core::style::Style *style) +{ + Fl_Browser *b = (Fl_Browser *)widget; + + FltkResource::setWidgetStyle(widget, style); + + b->textfont(widget->labelfont()); + b->textsize(widget->labelsize()); + b->textcolor(widget->labelcolor()); + + colWidths[0] = b->textsize(); + colWidths[1] = colWidths[0]; + colWidths[2] = colWidths[0]; + colWidths[3] = 0; +} + +void FltkListResource::widgetCallback (Fl_Widget *widget, void *data) +{ + Fl_Browser *b = (Fl_Browser *) widget; + + if (b->selected(b->value())) { + /* If it shouldn't be selectable, deselect it again. It would be nice to + * have a less unpleasant way to do this. + */ + const char *inactive_code; + if ((inactive_code = strstr(b->text(b->value()), "@N"))) { + const char *ignore_codes = strstr(b->text(b->value()), "@."); + + if (inactive_code < ignore_codes) + b->select(b->value(), 0); + } + } +} + +void *FltkListResource::newItem (const char *str, bool enabled, bool selected) +{ + Fl_Browser *b = (Fl_Browser *) widget; + int index = b->size() + 1; + char *label = (char *)malloc(strlen(str) + 1 + currDepth + 4), + *s = label; + + memset(s, '\a', currDepth); + s += currDepth; + if (!enabled) { + // FL_INACTIVE_COLOR + *s++ = '@'; + *s++ = 'N'; + } + // ignore further '@' chars + *s++ = '@'; + *s++ = '.'; + + strcpy(s, str); + + b->add(label); + free(label); + + if (selected) { + b->select(index, selected); + if (b->type() == FL_HOLD_BROWSER) { + /* Left to its own devices, it sometimes has some suboptimal ideas + * about how to scroll, and sometimes doesn't seem to show everything + * where it thinks it is. + */ + if (index > showRows) { + /* bottomline() and middleline() don't work because the widget is + * too tiny at this point for the bbox() call in + * Fl_Browser::lineposition() to do what one would want. + */ + b->topline(index - showRows + 1); + } else { + b->topline(1); + } + } + } + queueResize (true); + return NULL; +} + +void FltkListResource::addItem (const char *str, bool enabled, bool selected) +{ + // Fl_Browser_::incr_height() for item height won't do the right thing if + // the first item doesn't have anything to it. + if (!str || !*str) + str = " "; + newItem(str, enabled, selected); +} + +void FltkListResource::setItem (int index, bool selected) +{ + Fl_Browser *b = (Fl_Browser *) widget; + + b->select(index + 1, selected); +} + +void FltkListResource::pushGroup (const char *name, bool enabled) +{ + bool en = false; + bool selected = false; + + // Fl_Browser_::incr_height() for item height won't do the right thing if + // the first item doesn't have anything to it. + if (!name || !*name) + name = " "; + + // TODO: Proper disabling of item groups + newItem(name, en, selected); + + if (currDepth < 3) + currDepth++; +} + +void FltkListResource::popGroup () +{ + CustBrowser *b = (CustBrowser *) widget; + + newItem(" ", false, false); + b->hide(b->size()); + + if (currDepth) + currDepth--; +} + +int FltkListResource::getMaxItemWidth() +{ + return ((CustBrowser *) widget)->full_width(); +} + +void FltkListResource::sizeRequest (core::Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + if (style) { + CustBrowser *b = (CustBrowser *) widget; + int height = b->full_height(); + requisition->width = getMaxItemWidth() + 4; + + if (showRows * b->avg_height() < height) { + height = showRows * b->avg_height(); + b->has_scrollbar(Fl_Browser_::VERTICAL_ALWAYS); + requisition->width += Fl::scrollbar_size(); + } else { + b->has_scrollbar(0); + } + + requisition->descent = style->font->descent + 2; + requisition->ascent = height - style->font->descent + 2; + } else { + requisition->width = 1; + requisition->ascent = 1; + requisition->descent = 0; + } + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +int FltkListResource::getNumberOfItems() +{ + return ((Fl_Browser*)widget)->size(); +} + +bool FltkListResource::isSelected (int index) +{ + Fl_Browser *b = (Fl_Browser *) widget; + + return b->selected(index + 1) ? true : false; +} + +} // namespace ui +} // namespace fltk +} // namespace dw + diff --git a/dw/fltkui.hh b/dw/fltkui.hh new file mode 100644 index 0000000..fa47992 --- /dev/null +++ b/dw/fltkui.hh @@ -0,0 +1,505 @@ +#ifndef __DW_FLTK_UI_HH__ +#define __DW_FLTK_UI_HH__ + +#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__ +# error Do not include this file directly, use "fltkcore.hh" instead. +#endif + +#include <FL/Fl_Button.H> +#include <FL/Fl_Menu.H> +#include <FL/Fl_Text_Buffer.H> + +namespace dw { +namespace fltk { + +/** + * \brief FLTK implementation of dw::core::ui. + * + * <div style="border: 2px solid #ff0000; margin-top: 0.5em; + * margin-bottom: 0.5em; padding: 0.5em 1em; + * background-color: #ffefe0"><b>Update:</b> The complicated design + * results from my insufficient knowledge of C++ some years ago; since + * then, I've learned how to deal with "diamond inheritance", as the + * (ideal, not actually implemented) design in the first diagram + * shows. It should be possible to implement this ideal design in a + * straightforward way, and so get rid of templates. --SG</div> + * + * The design should be like this: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", dir="both", + * labelfontname=Helvetica, labelfontsize=10, color="#404040", + * labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_core { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::core::ui"; + * + * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"]; + * LabelButtonResource [color="#a0a0a0", + * URL="\ref dw::core::ui::LabelButtonResource"]; + * EntryResource [color="#a0a0a0", + * URL="\ref dw::core::ui::EntryResource"]; + * } + * + * subgraph cluster_fltk { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::fltk::ui"; + * + * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"]; + * FltkLabelButtonResource + * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"]; + * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"]; + * } + * + * Resource -> LabelButtonResource; + * Resource -> EntryResource; + * FltkResource -> FltkLabelButtonResource; + * FltkResource -> FltkEntryResource; + * Resource -> FltkResource; + * LabelButtonResource -> FltkLabelButtonResource; + * EntryResource -> FltkEntryResource; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * where dw::fltk::ui::FltkResource provides some base funtionality for all + * conctrete FLTK implementations of sub-interfaces of dw::core::ui::Resource. + * However, this is not directly possible in C++, since the base class + * dw::core::ui::Resource is ambiguous for + * dw::fltk::ui::FltkLabelButtonResource. + * + * To solve this, we have to remove the dependency between + * dw::fltk::ui::FltkResource and dw::core::ui::Resource, instead, the part + * of dw::core::ui::Resource, which is implemented in + * dw::fltk::ui::FltkResource, must be explicitly delegated from + * dw::fltk::ui::FltkLabelButtonResourceto dw::fltk::ui::FltkResource: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", dir="both", + * labelfontname=Helvetica, labelfontsize=10, color="#404040", + * labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_core { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::core::ui"; + * + * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"]; + * LabelButtonResource [color="#a0a0a0", + * URL="\ref dw::core::ui::LabelButtonResource"]; + * EntryResource [color="#a0a0a0", + * URL="\ref dw::core::ui::EntryResource"]; + * } + * + * subgraph cluster_fltk { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::fltk::ui"; + * + * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"]; + * FltkLabelButtonResource + * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"]; + * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"]; + * } + * + * Resource -> LabelButtonResource; + * Resource -> EntryResource; + * FltkResource -> FltkLabelButtonResource; + * FltkResource -> FltkEntryResource; + * LabelButtonResource -> FltkLabelButtonResource; + * EntryResource -> FltkEntryResource; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * To make this a bit simpler, we use templates: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", dir="both", + * labelfontname=Helvetica, labelfontsize=10, color="#404040", + * labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_core { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::core::ui"; + * + * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"]; + * LabelButtonResource [color="#a0a0a0", + * URL="\ref dw::core::ui::LabelButtonResource"]; + * EntryResource [color="#a0a0a0", + * URL="\ref dw::core::ui::EntryResource"]; + * } + * + * subgraph cluster_fltk { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::fltk::ui"; + * + * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"]; + * FltkSpecificResource [color="#a0a0a0", + * fillcolor="#ffffc0", style="filled" + * URL="\ref dw::fltk::ui::FltkSpecificResource"]; + * FltkSpecificResource_button [color="#a0a0a0", + * label="FltkSpecificResource \<LabelButtonResource\>"]; + * FltkSpecificResource_entry [color="#a0a0a0", + * label="FltkSpecificResource \<EntryResource\>"]; + * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"]; + * FltkLabelButtonResource + * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"]; + * } + * + * Resource -> LabelButtonResource; + * Resource -> EntryResource; + * FltkResource -> FltkSpecificResource; + * FltkSpecificResource -> FltkSpecificResource_button [arrowhead="open", + * arrowtail="none", + * dir="both", + * style="dashed", + * color="#808000"]; + * FltkSpecificResource -> FltkSpecificResource_entry [arrowhead="open", + * arrowtail="none", + * dir="both", + * style="dashed", + * color="#808000"]; + * LabelButtonResource -> FltkSpecificResource_button; + * EntryResource -> FltkSpecificResource_entry; + * FltkSpecificResource_button -> FltkLabelButtonResource; + * FltkSpecificResource_entry -> FltkEntryResource; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + */ +namespace ui { + +/** + * ... + */ +class FltkResource: public lout::object::Object +{ +private: + bool enabled; + +protected: + FltkView *view; + Fl_Widget *widget; + core::Allocation allocation; + FltkPlatform *platform; + + core::style::Style *style; + + FltkResource (FltkPlatform *platform); + void init (FltkPlatform *platform); + virtual Fl_Widget *createNewWidget (core::Allocation *allocation) = 0; + + virtual void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + void setDisplayed (bool displayed); + bool displayed(); +public: + ~FltkResource (); + + virtual void attachView (FltkView *view); + virtual void detachView (FltkView *view); + + void sizeAllocate (core::Allocation *allocation); + void draw (core::View *view, core::Rectangle *area); + + void setStyle (core::style::Style *style); + + bool isEnabled (); + void setEnabled (bool enabled); +}; + + +template <class I> class FltkSpecificResource: public I, public FltkResource +{ +public: + FltkSpecificResource (FltkPlatform *platform); + ~FltkSpecificResource (); + + void sizeAllocate (core::Allocation *allocation); + void draw (core::View *view, core::Rectangle *area); + void setStyle (core::style::Style *style); + + bool isEnabled (); + void setEnabled (bool enabled); +}; + + +class FltkLabelButtonResource: + public FltkSpecificResource <dw::core::ui::LabelButtonResource> +{ +private: + const char *label; + + static void widgetCallback (Fl_Widget *widget, void *data); + +protected: + Fl_Widget *createNewWidget (core::Allocation *allocation); + +public: + FltkLabelButtonResource (FltkPlatform *platform, const char *label); + ~FltkLabelButtonResource (); + + void sizeRequest (core::Requisition *requisition); + + const char *getLabel (); + void setLabel (const char *label); +}; + +/** + * \bug Maximal length not supported yet. + * \todo Text values are not synchronized (not needed in dillo). + */ +class FltkEntryResource: + public FltkSpecificResource <dw::core::ui::EntryResource> +{ +private: + int size; + bool password; + const char *initText; + char *label; + int label_w; + bool editable; + + static void widgetCallback (Fl_Widget *widget, void *data); + void setDisplayed (bool displayed); + +protected: + Fl_Widget *createNewWidget (core::Allocation *allocation); + void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + +public: + FltkEntryResource (FltkPlatform *platform, int size, bool password, + const char *label); + ~FltkEntryResource (); + + void sizeRequest (core::Requisition *requisition); + void sizeAllocate (core::Allocation *allocation); + + const char *getText (); + void setText (const char *text); + bool isEditable (); + void setEditable (bool editable); + void setMaxLength (int maxlen); +}; + + +class FltkMultiLineTextResource: + public FltkSpecificResource <dw::core::ui::MultiLineTextResource> +{ +private: + Fl_Text_Buffer *buffer; + char *text_copy; + bool editable; + int numCols, numRows; + +protected: + Fl_Widget *createNewWidget (core::Allocation *allocation); + void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + +public: + FltkMultiLineTextResource (FltkPlatform *platform, int cols, int rows); + ~FltkMultiLineTextResource (); + + void sizeRequest (core::Requisition *requisition); + + const char *getText (); + void setText (const char *text); + bool isEditable (); + void setEditable (bool editable); +}; + + +template <class I> class FltkToggleButtonResource: + public FltkSpecificResource <I> +{ +private: + bool initActivated; + +protected: + virtual Fl_Button *createNewButton (core::Allocation *allocation) = 0; + Fl_Widget *createNewWidget (core::Allocation *allocation); + void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + +public: + FltkToggleButtonResource (FltkPlatform *platform, + bool activated); + ~FltkToggleButtonResource (); + + void sizeRequest (core::Requisition *requisition); + + bool isActivated (); + void setActivated (bool activated); +}; + + +class FltkCheckButtonResource: + public FltkToggleButtonResource <dw::core::ui::CheckButtonResource> +{ +protected: + Fl_Button *createNewButton (core::Allocation *allocation); + +public: + FltkCheckButtonResource (FltkPlatform *platform, + bool activated); + ~FltkCheckButtonResource (); +}; + + +class FltkRadioButtonResource: + public FltkToggleButtonResource <dw::core::ui::RadioButtonResource> +{ +private: + class Group + { + private: + class FltkGroupIterator: + public dw::core::ui::RadioButtonResource::GroupIterator + { + private: + lout::container::typed::Iterator <FltkRadioButtonResource> it; + + public: + inline FltkGroupIterator (lout::container::typed::List + <FltkRadioButtonResource> + *list) + { it = list->iterator (); } + + bool hasNext (); + dw::core::ui::RadioButtonResource *getNext (); + void unref (); + }; + + lout::container::typed::List <FltkRadioButtonResource> *list; + + protected: + ~Group (); + + public: + Group (FltkRadioButtonResource *radioButtonResource); + + inline lout::container::typed::Iterator <FltkRadioButtonResource> + iterator () + { + return list->iterator (); + } + + inline dw::core::ui::RadioButtonResource::GroupIterator + *groupIterator () + { + return new FltkGroupIterator (list); + } + + void connect (FltkRadioButtonResource *radioButtonResource); + void unconnect (FltkRadioButtonResource *radioButtonResource); + }; + + Group *group; + + static void widgetCallback (Fl_Widget *widget, void *data); + void buttonClicked (); + +protected: + Fl_Button *createNewButton (core::Allocation *allocation); + +public: + FltkRadioButtonResource (FltkPlatform *platform, + FltkRadioButtonResource *groupedWith, + bool activated); + ~FltkRadioButtonResource (); + + GroupIterator *groupIterator (); +}; + + +template <class I> class FltkSelectionResource: + public FltkSpecificResource <I> +{ +protected: + virtual bool setSelectedItems() { return false; } + virtual void addItem (const char *str, bool enabled, bool selected) = 0; + virtual void setItem (int index, bool selected) = 0; + virtual void pushGroup (const char *name, bool enabled) = 0; + virtual void popGroup () = 0; +public: + FltkSelectionResource (FltkPlatform *platform) : + FltkSpecificResource<I> (platform) {}; + dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd); +}; + + +class FltkOptionMenuResource: + public FltkSelectionResource <dw::core::ui::OptionMenuResource> +{ +protected: + Fl_Widget *createNewWidget (core::Allocation *allocation); + virtual bool setSelectedItems() { return true; } + void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + int getNumberOfItems(); + int getMaxItemWidth (); +private: + static void widgetCallback (Fl_Widget *widget, void *data); + void enlargeMenu(); + Fl_Menu_Item *newItem(); + Fl_Menu_Item *menu; + int itemsAllocated, itemsUsed; +public: + FltkOptionMenuResource (FltkPlatform *platform); + ~FltkOptionMenuResource (); + + void addItem (const char *str, bool enabled, bool selected); + void setItem (int index, bool selected); + void pushGroup (const char *name, bool enabled); + void popGroup (); + + void sizeRequest (core::Requisition *requisition); + bool isSelected (int index); +}; + +class FltkListResource: + public FltkSelectionResource <dw::core::ui::ListResource> +{ +protected: + Fl_Widget *createNewWidget (core::Allocation *allocation); + void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); + int getNumberOfItems(); + int getMaxItemWidth (); +private: + static void widgetCallback (Fl_Widget *widget, void *data); + void *newItem (const char *str, bool enabled, bool selected); + int currDepth; + int colWidths[4]; + int showRows; + ListResource::SelectionMode mode; +public: + FltkListResource (FltkPlatform *platform, + core::ui::ListResource::SelectionMode selectionMode, + int rows); + ~FltkListResource (); + + void addItem (const char *str, bool enabled, bool selected); + void setItem (int index, bool selected); + void pushGroup (const char *name, bool enabled); + void popGroup (); + + void sizeRequest (core::Requisition *requisition); + bool isSelected (int index); +}; + + +} // namespace ui +} // namespace fltk +} // namespace dw + + +#endif // __DW_FLTK_UI_HH__ diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc new file mode 100644 index 0000000..3b1c0cc --- /dev/null +++ b/dw/fltkviewbase.cc @@ -0,0 +1,744 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "fltkviewport.hh" + +#include <FL/Fl.H> +#include <FL/fl_draw.H> + +#include <stdio.h> +#include "../lout/msg.h" + +extern Fl_Widget* fl_oldfocus; + +using namespace lout::object; +using namespace lout::container::typed; + +namespace dw { +namespace fltk { + +FltkViewBase::BackBuffer::BackBuffer () +{ + w = 0; + h = 0; + created = false; +} + +FltkViewBase::BackBuffer::~BackBuffer () +{ + if (created) + fl_delete_offscreen (offscreen); +} + +void FltkViewBase::BackBuffer::setSize (int w, int h) +{ + if (!created || w > this->w || h > this->h) { + this->w = w; + this->h = h; + if (created) + fl_delete_offscreen (offscreen); + offscreen = fl_create_offscreen (w, h); + created = true; + } +} + +FltkViewBase::BackBuffer *FltkViewBase::backBuffer; +bool FltkViewBase::backBufferInUse; + +FltkViewBase::FltkViewBase (int x, int y, int w, int h, const char *label): + Fl_Group (x, y, w, h, label) +{ + Fl_Group::current(0); + canvasWidth = 1; + canvasHeight = 1; + bgColor = FL_WHITE; + mouse_x = mouse_y = 0; + focused_child = NULL; + exposeArea = NULL; + if (backBuffer == NULL) { + backBuffer = new BackBuffer (); + } + box(FL_NO_BOX); + resizable(NULL); +} + +FltkViewBase::~FltkViewBase () +{ + cancelQueueDraw (); +} + +void FltkViewBase::setBufferedDrawing (bool b) { + if (b && backBuffer == NULL) { + backBuffer = new BackBuffer (); + } else if (!b && backBuffer != NULL) { + delete backBuffer; + backBuffer = NULL; + } +} + +void FltkViewBase::draw () +{ + int d = damage (); + + if ((d & FL_DAMAGE_USER1) && !(d & FL_DAMAGE_EXPOSE)) { + lout::container::typed::Iterator <core::Rectangle> it; + + for (it = drawRegion.rectangles (); it.hasNext (); ) { + draw (it.getNext (), DRAW_BUFFERED); + } + + drawRegion.clear (); + d &= ~FL_DAMAGE_USER1; + } + + if (d & FL_DAMAGE_CHILD) { + drawChildWidgets (); + d &= ~FL_DAMAGE_CHILD; + } + + if (d) { + dw::core::Rectangle rect ( + translateViewXToCanvasX (x ()), + translateViewYToCanvasY (y ()), + w (), + h ()); + + if (d == FL_DAMAGE_SCROLL) { + // a clipping rectangle has already been set by fltk::scrollrect () + draw (&rect, DRAW_PLAIN); + } else { + draw (&rect, DRAW_CLIPPED); + drawRegion.clear (); + } + } +} + +void FltkViewBase::draw (const core::Rectangle *rect, + DrawType type) +{ + int X = translateCanvasXToViewX (rect->x); + int Y = translateCanvasYToViewY (rect->y); + int W, H; + + // fl_clip_box() can't handle values greater than SHRT_MAX! + if (X > x () + w () || Y > y () + h ()) + return; + + W = X + rect->width > x () + w () ? x () + w () - X : rect->width; + H = Y + rect->height > y () + h () ? y () + h () - Y : rect->height; + + fl_clip_box(X, Y, W, H, X, Y, W, H); + + core::Rectangle r (translateViewXToCanvasX (X), + translateViewYToCanvasY (Y), W, H); + + if (r.isEmpty ()) + return; + + exposeArea = &r; + + if (type == DRAW_BUFFERED && backBuffer && !backBufferInUse) { + backBufferInUse = true; + backBuffer->setSize (X + W, Y + H); // would be nicer to use (W, H)... + fl_begin_offscreen (backBuffer->offscreen); + fl_push_matrix (); + fl_color (bgColor); + fl_rectf (X, Y, W, H); + theLayout->expose (this, &r); + fl_pop_matrix (); + fl_end_offscreen (); + fl_copy_offscreen (X, Y, W, H, backBuffer->offscreen, X, Y); + backBufferInUse = false; + } else if (type == DRAW_BUFFERED || type == DRAW_CLIPPED) { + // if type == DRAW_BUFFERED but we do not have backBuffer available + // we fall back to clipped drawing + fl_push_clip (X, Y, W, H); + fl_color (bgColor); + fl_rectf (X, Y, W, H); + theLayout->expose (this, &r); + fl_pop_clip (); + } else { + fl_color (bgColor); + fl_rectf (X, Y, W, H); + theLayout->expose (this, &r); + } + // DEBUG: + //fl_color(FL_RED); + //fl_rect(X, Y, W, H); + + exposeArea = NULL; +} + +void FltkViewBase::drawChildWidgets () { + for (int i = children () - 1; i >= 0; i--) { + Fl_Widget& w = *child(i); +#if 0 +PORT1.3 + if (w.damage() & DAMAGE_CHILD_LABEL) { + draw_outside_label(w); + w.set_damage(w.damage() & ~DAMAGE_CHILD_LABEL); + } +#endif + update_child(w); + } +} + +core::ButtonState getDwButtonState () +{ + int s1 = Fl::event_state (); + int s2 = (core::ButtonState)0; + + if (s1 & FL_SHIFT) s2 |= core::SHIFT_MASK; + if (s1 & FL_CTRL) s2 |= core::CONTROL_MASK; + if (s1 & FL_ALT) s2 |= core::META_MASK; + if (s1 & FL_BUTTON1) s2 |= core::BUTTON1_MASK; + if (s1 & FL_BUTTON2) s2 |= core::BUTTON2_MASK; + if (s1 & FL_BUTTON3) s2 |= core::BUTTON3_MASK; + + return (core::ButtonState)s2; +} + +/* + * We handle Tab to determine which FLTK widget should get focus. + * + * Presumably a proper solution that allows focusing links, etc., would live + * in Textblock and use iterators. + */ +int FltkViewBase::manageTabToFocus() +{ + int i, ret = 0; + Fl_Widget *old_child = NULL; + + if (this == Fl::focus()) { + // if we have focus, give it to a child. Go forward typically, + // or backward with Shift pressed. + if (!(Fl::event_state() & FL_SHIFT)) { + for (i = 0; i < children(); i++) { + if (child(i)->take_focus()) { + ret = 1; + break; + } + } + } else { + for (i = children() - 1; i >= 0; i--) { + if (child(i)->take_focus()) { + ret = 1; + break; + } + } + } + } else { + // tabbing between children + old_child = Fl::focus(); + + if (!(ret = Fl_Group::handle (FL_KEYBOARD))) { + // group didn't have any more children to focus. + Fl::focus(this); + return 1; + } else { + // which one did it focus? (Note i == children() if not found) + i = find(Fl::focus()); + } + } + if (ret) { + if (i >= 0 && i < children()) { + Fl_Widget *c = child(i); + int canvasX = translateViewXToCanvasX(c->x()), + canvasY = translateViewYToCanvasY(c->y()); + + theLayout->scrollTo(core::HPOS_INTO_VIEW, core::VPOS_INTO_VIEW, + canvasX, canvasY, c->w(), c->h()); + + // Draw the children who gained and lost focus. Otherwise a + // widget that had been only partly visible still shows its old + // appearance in the previously-visible portion. + core::Rectangle r(canvasX, canvasY, c->w(), c->h()); + + queueDraw(&r); + + if (old_child) { + r.x = translateViewXToCanvasX(old_child->x()); + r.y = translateViewYToCanvasY(old_child->y()); + r.width = old_child->w(); + r.height = old_child->h(); + queueDraw(&r); + } + } + } + return ret; +} + +int FltkViewBase::handle (int event) +{ + bool processed; + + /** + * \todo Consider, whether this from the FLTK documentation has any + * impacts: "To receive fltk::RELEASE events you must return non-zero + * when passed a fltk::PUSH event. " + */ + switch(event) { + case FL_PUSH: + /* Hide the tooltip */ + theLayout->cancelTooltip(); + + processed = + theLayout->buttonPress (this, Fl::event_clicks () + 1, + translateViewXToCanvasX (Fl::event_x ()), + translateViewYToCanvasY (Fl::event_y ()), + getDwButtonState (), Fl::event_button ()); + _MSG("PUSH => %s\n", processed ? "true" : "false"); + if (processed) { + /* pressed dw content; give focus to the view */ + if (Fl::event_button() != FL_RIGHT_MOUSE) + Fl::focus(this); + return true; + } + break; + case FL_RELEASE: + processed = + theLayout->buttonRelease (this, Fl::event_clicks () + 1, + translateViewXToCanvasX (Fl::event_x ()), + translateViewYToCanvasY (Fl::event_y ()), + getDwButtonState (), Fl::event_button ()); + _MSG("RELEASE => %s\n", processed ? "true" : "false"); + if (processed) + return true; + break; + case FL_MOVE: + mouse_x = Fl::event_x(); + mouse_y = Fl::event_y(); + processed = + theLayout->motionNotify (this, + translateViewXToCanvasX (mouse_x), + translateViewYToCanvasY (mouse_y), + getDwButtonState ()); + _MSG("MOVE => %s\n", processed ? "true" : "false"); + if (processed) + return true; + break; + case FL_DRAG: + processed = + theLayout->motionNotify (this, + translateViewXToCanvasX (Fl::event_x ()), + translateViewYToCanvasY (Fl::event_y ()), + getDwButtonState ()); + _MSG("DRAG => %s\n", processed ? "true" : "false"); + if (processed) + return true; + break; + case FL_ENTER: + theLayout->enterNotify (this, + translateViewXToCanvasX (Fl::event_x ()), + translateViewYToCanvasY (Fl::event_y ()), + getDwButtonState ()); + break; + case FL_HIDE: + /* WORKAROUND: strangely, the tooltip window is not automatically hidden + * with its parent. Here we fake a LEAVE to achieve it. */ + case FL_LEAVE: + theLayout->leaveNotify (this, getDwButtonState ()); + break; + case FL_FOCUS: + if (focused_child && find(focused_child) < children()) { + /* strangely, find() == children() if the child is not found */ + focused_child->take_focus(); + } + return 1; + case FL_UNFOCUS: + focused_child = fl_oldfocus; + return 0; + case FL_KEYBOARD: + if (Fl::event_key() == FL_Tab) + return manageTabToFocus(); + break; + default: + break; + } + return Fl_Group::handle (event); +} + +// ---------------------------------------------------------------------- + +void FltkViewBase::setLayout (core::Layout *layout) +{ + theLayout = layout; + if (usesViewport()) + theLayout->viewportSizeChanged(this, w(), h()); +} + +void FltkViewBase::setCanvasSize (int width, int ascent, int descent) +{ + canvasWidth = width; + canvasHeight = ascent + descent; +} + +void FltkViewBase::setCursor (core::style::Cursor cursor) +{ + static Fl_Cursor mapDwToFltk[] = { + FL_CURSOR_CROSS, + FL_CURSOR_DEFAULT, + FL_CURSOR_HAND, + FL_CURSOR_MOVE, + FL_CURSOR_WE, + FL_CURSOR_NESW, + FL_CURSOR_NWSE, + FL_CURSOR_NS, + FL_CURSOR_NWSE, + FL_CURSOR_NESW, + FL_CURSOR_NS, + FL_CURSOR_WE, + FL_CURSOR_INSERT, + FL_CURSOR_WAIT, + FL_CURSOR_HELP + }; + + fl_cursor (mapDwToFltk[cursor]); +} + +void FltkViewBase::setBgColor (core::style::Color *color) +{ + bgColor = color ? + ((FltkColor*)color)->colors[dw::core::style::Color::SHADING_NORMAL] : + FL_WHITE; +} + +void FltkViewBase::startDrawing (core::Rectangle *area) +{ +} + +void FltkViewBase::finishDrawing (core::Rectangle *area) +{ +} + +void FltkViewBase::queueDraw (core::Rectangle *area) +{ + drawRegion.addRectangle (area); + damage (FL_DAMAGE_USER1); +} + +void FltkViewBase::queueDrawTotal () +{ + damage (FL_DAMAGE_EXPOSE); +} + +void FltkViewBase::cancelQueueDraw () +{ +} + +void FltkViewBase::drawPoint (core::style::Color *color, + core::style::Color::Shading shading, + int x, int y) +{ +} + +void FltkViewBase::drawLine (core::style::Color *color, + core::style::Color::Shading shading, + int x1, int y1, int x2, int y2) +{ + fl_color(((FltkColor*)color)->colors[shading]); + // we clip with a large border (5000px), as clipping causes artefacts + // with non-solid line styles. + // However it's still better than no clipping at all. + clipPoint (&x1, &y1, 5000); + clipPoint (&x2, &y2, 5000); + fl_line (translateCanvasXToViewX (x1), + translateCanvasYToViewY (y1), + translateCanvasXToViewX (x2), + translateCanvasYToViewY (y2)); +} + +void FltkViewBase::drawTypedLine (core::style::Color *color, + core::style::Color::Shading shading, + core::style::LineType type, int width, + int x1, int y1, int x2, int y2) +{ + char dashes[3], w, ng, d, gap, len; + const int f = 2; + + w = (width == 1) ? 0 : width; + if (type == core::style::LINE_DOTTED) { + /* customized drawing for dotted lines */ + len = (x2 == x1) ? y2 - y1 + 1 : (y2 == y1) ? x2 - x1 + 1 : 0; + ng = len / f*width; + d = len % f*width; + gap = ng ? d/ng + (w > 3 ? 2 : 0) : 0; + dashes[0] = 1; dashes[1] = f*width-gap; dashes[2] = 0; + fl_line_style(FL_DASH + FL_CAP_ROUND, w, dashes); + + /* These formulas also work, but ain't pretty ;) + * fl_line_style(FL_DOT + FL_CAP_ROUND, w); + * dashes[0] = 1; dashes[1] = 3*width-2; dashes[2] = 0; + */ + } else if (type == core::style::LINE_DASHED) { + fl_line_style(FL_DASH + FL_CAP_ROUND, w); + } + + fl_color(((FltkColor*)color)->colors[shading]); + drawLine (color, shading, x1, y1, x2, y2); + + if (type != core::style::LINE_NORMAL) + fl_line_style(FL_SOLID); +} + +void FltkViewBase::drawRectangle (core::style::Color *color, + core::style::Color::Shading shading, + bool filled, + int X, int Y, int width, int height) +{ + fl_color(((FltkColor*)color)->colors[shading]); + if (width < 0) { + X += width; + width = -width; + } + if (height < 0) { + Y += height; + height = -height; + } + + int x1 = X; + int y1 = Y; + int x2 = X + width; + int y2 = Y + height; + + // We only support rectangles with line width 1px, so we clip with + // a rectangle 1px wider and higher than what we actually expose. + // This is only really necessary for non-filled rectangles. + clipPoint (&x1, &y1, 1); + clipPoint (&x2, &y2, 1); + + x1 = translateCanvasXToViewX (x1); + y1 = translateCanvasYToViewY (y1); + x2 = translateCanvasXToViewX (x2); + y2 = translateCanvasYToViewY (y2); + + if (filled) + fl_rectf (x1, y1, x2 - x1, y2 - y1); + else + fl_rect (x1, y1, x2 - x1, y2 - y1); +} + +void FltkViewBase::drawArc (core::style::Color *color, + core::style::Color::Shading shading, bool filled, + int centerX, int centerY, int width, int height, + int angle1, int angle2) +{ + fl_color(((FltkColor*)color)->colors[shading]); + int x = translateCanvasXToViewX (centerX) - width / 2; + int y = translateCanvasYToViewY (centerY) - height / 2; + + fl_arc(x, y, width, height, angle1, angle2); + if (filled) { + // WORKAROUND: We call both fl_arc and fl_pie due to a FLTK bug + // (STR #2703) that was present in 1.3.0. + fl_pie(x, y, width, height, angle1, angle2); + } +} + +void FltkViewBase::drawPolygon (core::style::Color *color, + core::style::Color::Shading shading, + bool filled, bool convex, core::Point *points, + int npoints) +{ + if (npoints > 0) { + fl_color(((FltkColor*)color)->colors[shading]); + + if (filled) { + if (convex) + fl_begin_polygon(); + else + fl_begin_complex_polygon(); + } else + fl_begin_loop(); + + for (int i = 0; i < npoints; i++) { + fl_vertex(translateCanvasXToViewX(points[i].x), + translateCanvasYToViewY(points[i].y)); + } + if (filled) { + if (convex) + fl_end_polygon(); + else + fl_end_complex_polygon(); + } else + fl_end_loop(); + } +} + +core::View *FltkViewBase::getClippingView (int x, int y, int width, int height) +{ + fl_push_clip (translateCanvasXToViewX (x), translateCanvasYToViewY (y), + width, height); + return this; +} + +void FltkViewBase::mergeClippingView (core::View *clippingView) +{ + fl_pop_clip (); +} + +// ---------------------------------------------------------------------- + +FltkWidgetView::FltkWidgetView (int x, int y, int w, int h, + const char *label): + FltkViewBase (x, y, w, h, label) +{ +} + +FltkWidgetView::~FltkWidgetView () +{ +} + +void FltkWidgetView::drawText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int X, int Y, const char *text, int len) +{ + //printf ("drawText (..., %d, %d, '", X, Y); + //for (int i = 0; i < len; i++) + // putchar (text[i]); + //printf ("'\n"); + + FltkFont *ff = (FltkFont*)font; + fl_font(ff->font, ff->size); + fl_color(((FltkColor*)color)->colors[shading]); + + if (!font->letterSpacing && !font->fontVariant) { + fl_draw(text, len, + translateCanvasXToViewX (X), translateCanvasYToViewY (Y)); + } else { + /* Nonzero letter spacing adjustment, draw each glyph individually */ + int viewX = translateCanvasXToViewX (X), + viewY = translateCanvasYToViewY (Y); + int curr = 0, next = 0, nb; + char chbuf[4]; + int c, cu, width; + + if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) { + int sc_fontsize = lout::misc::roundInt(ff->size * 0.78); + for (curr = 0; next < len; curr = next) { + next = theLayout->nextGlyph(text, curr); + c = fl_utf8decode(text + curr, text + next, &nb); + if ((cu = fl_toupper(c)) == c) { + /* already uppercase, just draw the character */ + fl_font(ff->font, ff->size); + width = (int)fl_width(text + curr, next - curr); + if (curr && width) + viewX += font->letterSpacing; + fl_draw(text + curr, next - curr, viewX, viewY); + viewX += width; + } else { + /* make utf8 string for converted char */ + nb = fl_utf8encode(cu, chbuf); + fl_font(ff->font, sc_fontsize); + width = (int)fl_width(chbuf, nb); + if (curr && width) + viewX += font->letterSpacing; + fl_draw(chbuf, nb, viewX, viewY); + viewX += width; + } + } + } else { + while (next < len) { + next = theLayout->nextGlyph(text, curr); + width = (int)fl_width(text + curr, next - curr); + if (curr && width) + viewX += font->letterSpacing; + fl_draw(text + curr, next - curr, viewX, viewY); + viewX += width; + curr = next; + } + } + } +} + +/* + * "simple" in that it ignores letter-spacing, etc. This was added for image + * alt text where none of that matters. + */ +void FltkWidgetView::drawSimpleWrappedText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int X, int Y, int W, int H, + const char *text) +{ + FltkFont *ff = (FltkFont*)font; + fl_font(ff->font, ff->size); + fl_color(((FltkColor*)color)->colors[shading]); + fl_draw(text, + translateCanvasXToViewX (X), translateCanvasYToViewY (Y), + W, H, FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_WRAP, NULL, 0); +} + +void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, + int X, int Y, int width, int height) +{ + ((FltkImgbuf*)imgbuf)->draw (this, + translateCanvasXToViewX (xRoot), + translateCanvasYToViewY (yRoot), + X, Y, width, height); +} + +bool FltkWidgetView::usesFltkWidgets () +{ + return true; +} + +void FltkWidgetView::addFltkWidget (Fl_Widget *widget, + core::Allocation *allocation) +{ + allocateFltkWidget (widget, allocation); + add (widget); +} + +void FltkWidgetView::removeFltkWidget (Fl_Widget *widget) +{ + remove (widget); +} + +void FltkWidgetView::allocateFltkWidget (Fl_Widget *widget, + core::Allocation *allocation) +{ + widget->resize (translateCanvasXToViewX (allocation->x), + translateCanvasYToViewY (allocation->y), + allocation->width, + allocation->ascent + allocation->descent); +} + +void FltkWidgetView::drawFltkWidget (Fl_Widget *widget, + core::Rectangle *area) +{ + draw_child (*widget); + draw_outside_label(*widget); +} + +} // namespace fltk +} // namespace dw diff --git a/dw/fltkviewbase.hh b/dw/fltkviewbase.hh new file mode 100644 index 0000000..eb4ec32 --- /dev/null +++ b/dw/fltkviewbase.hh @@ -0,0 +1,141 @@ +#ifndef __DW_FLTKVIEWBASE_HH__ +#define __DW_FLTKVIEWBASE_HH__ + +#include <time.h> // for time_t +#include <sys/time.h> // for time_t in FreeBSD + +#include <FL/Fl_Group.H> +#include <FL/x.H> + +#include "fltkcore.hh" + +namespace dw { +namespace fltk { + +class FltkViewBase: public FltkView, public Fl_Group +{ +private: + class BackBuffer { + private: + int w; + int h; + bool created; + + public: + Fl_Offscreen offscreen; + + BackBuffer (); + ~BackBuffer (); + void setSize(int w, int h); + }; + + typedef enum { DRAW_PLAIN, DRAW_CLIPPED, DRAW_BUFFERED } DrawType; + + int bgColor; + core::Region drawRegion; + core::Rectangle *exposeArea; + static BackBuffer *backBuffer; + static bool backBufferInUse; + + void draw (const core::Rectangle *rect, DrawType type); + void drawChildWidgets (); + int manageTabToFocus(); + inline void clipPoint (int *x, int *y, int border) { + if (exposeArea) { + if (*x < exposeArea->x - border) + *x = exposeArea->x - border; + if (*x > exposeArea->x + exposeArea->width + border) + *x = exposeArea->x + exposeArea->width + border; + if (*y < exposeArea->y - border) + *y = exposeArea->y - border; + if (*y > exposeArea->y + exposeArea->height + border) + *y = exposeArea->y + exposeArea->height + border; + } + } +protected: + core::Layout *theLayout; + int canvasWidth, canvasHeight; + int mouse_x, mouse_y; + Fl_Widget *focused_child; + + virtual int translateViewXToCanvasX (int x) = 0; + virtual int translateViewYToCanvasY (int y) = 0; + virtual int translateCanvasXToViewX (int x) = 0; + virtual int translateCanvasYToViewY (int y) = 0; + +public: + FltkViewBase (int x, int y, int w, int h, const char *label = 0); + ~FltkViewBase (); + + void draw(); + int handle (int event); + + void setLayout (core::Layout *layout); + void setCanvasSize (int width, int ascent, int descent); + void setCursor (core::style::Cursor cursor); + void setBgColor (core::style::Color *color); + + void startDrawing (core::Rectangle *area); + void finishDrawing (core::Rectangle *area); + void queueDraw (core::Rectangle *area); + void queueDrawTotal (); + void cancelQueueDraw (); + void drawPoint (core::style::Color *color, + core::style::Color::Shading shading, + int x, int y); + void drawLine (core::style::Color *color, + core::style::Color::Shading shading, + int x1, int y1, int x2, int y2); + void drawTypedLine (core::style::Color *color, + core::style::Color::Shading shading, + core::style::LineType type, int width, + int x1, int y1, int x2, int y2); + void drawRectangle (core::style::Color *color, + core::style::Color::Shading shading, bool filled, + int x, int y, int width, int height); + void drawArc (core::style::Color *color, + core::style::Color::Shading shading, bool filled, + int centerX, int centerY, int width, int height, + int angle1, int angle2); + void drawPolygon (core::style::Color *color, + core::style::Color::Shading shading, + bool filled, bool convex, + core::Point *points, int npoints); + + core::View *getClippingView (int x, int y, int width, int height); + void mergeClippingView (core::View *clippingView); + void setBufferedDrawing (bool b); +}; + + +class FltkWidgetView: public FltkViewBase +{ +public: + FltkWidgetView (int x, int y, int w, int h, const char *label = 0); + ~FltkWidgetView (); + + void drawText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, const char *text, int len); + void drawSimpleWrappedText (core::style::Font *font, + core::style::Color *color, + core::style::Color::Shading shading, + int x, int y, int w, int h, + const char *text); + void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, + int x, int y, int width, int height); + + bool usesFltkWidgets (); + void addFltkWidget (Fl_Widget *widget, core::Allocation *allocation); + void removeFltkWidget (Fl_Widget *widget); + void allocateFltkWidget (Fl_Widget *widget, + core::Allocation *allocation); + void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area); +}; + +} // namespace fltk +} // namespace dw + +#endif // __DW_FLTKVIEWBASE_HH__ + diff --git a/dw/fltkviewport.cc b/dw/fltkviewport.cc new file mode 100644 index 0000000..804b62f --- /dev/null +++ b/dw/fltkviewport.cc @@ -0,0 +1,558 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "fltkviewport.hh" + +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/names.h> + +#include <stdio.h> +#include "../lout/msg.h" +#include "../lout/debug.hh" + +using namespace lout; +using namespace lout::object; +using namespace lout::container::typed; + +namespace dw { +namespace fltk { + +/* + * Lets SHIFT+{Left,Right} go to the parent + */ +class CustScrollbar : public Fl_Scrollbar +{ +public: + CustScrollbar(int x, int y, int w, int h) : Fl_Scrollbar(x,y,w,h) {}; + int handle(int e) { + if (e == FL_SHORTCUT && Fl::event_state() == FL_SHIFT && + (Fl::event_key() == FL_Left || Fl::event_key() == FL_Right)) + return 0; + return Fl_Scrollbar::handle(e); + } +}; + +FltkViewport::FltkViewport (int X, int Y, int W, int H, const char *label): + FltkWidgetView (X, Y, W, H, label) +{ + DBG_OBJ_CREATE ("dw::fltk::FltkViewport"); + + hscrollbar = new CustScrollbar (x (), y (), 1, 1); + hscrollbar->type(FL_HORIZONTAL); + hscrollbar->callback (hscrollbarCallback, this); + hscrollbar->hide(); + add (hscrollbar); + + vscrollbar = new Fl_Scrollbar (x (), y(), 1, 1); + vscrollbar->type(FL_VERTICAL); + vscrollbar->callback (vscrollbarCallback, this); + vscrollbar->hide(); + add (vscrollbar); + + hasDragScroll = 1; + scrollX = scrollY = scrollDX = scrollDY = 0; + horScrolling = verScrolling = dragScrolling = 0; + + gadgetOrientation[0] = GADGET_HORIZONTAL; + gadgetOrientation[1] = GADGET_HORIZONTAL; + gadgetOrientation[2] = GADGET_VERTICAL; + gadgetOrientation[3] = GADGET_HORIZONTAL; + + gadgets = + new container::typed::List <object::TypedPointer < Fl_Widget> > + (true); +} + +FltkViewport::~FltkViewport () +{ + delete gadgets; + DBG_OBJ_DELETE (); +} + +void FltkViewport::adjustScrollbarsAndGadgetsAllocation () +{ + int hdiff = 0, vdiff = 0; + int visibility = 0; + + _MSG(" >>FltkViewport::adjustScrollbarsAndGadgetsAllocation\n"); + if (hscrollbar->visible ()) + visibility |= 1; + if (vscrollbar->visible ()) + visibility |= 2; + + if (gadgets->size () > 0) { + switch (gadgetOrientation [visibility]) { + case GADGET_VERTICAL: + hdiff = SCROLLBAR_THICKNESS; + vdiff = SCROLLBAR_THICKNESS * gadgets->size (); + break; + + case GADGET_HORIZONTAL: + hdiff = SCROLLBAR_THICKNESS * gadgets->size (); + vdiff = SCROLLBAR_THICKNESS; + break; + } + } else { + hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + } + + hscrollbar->resize(x (), y () + h () - SCROLLBAR_THICKNESS, + w () - hdiff, SCROLLBAR_THICKNESS); + vscrollbar->resize(x () + w () - SCROLLBAR_THICKNESS, y (), + SCROLLBAR_THICKNESS, h () - vdiff); + + int X = x () + w () - SCROLLBAR_THICKNESS; + int Y = y () + h () - SCROLLBAR_THICKNESS; + for (Iterator <TypedPointer < Fl_Widget> > it = gadgets->iterator (); + it.hasNext (); ) { + Fl_Widget *widget = it.getNext()->getTypedValue (); + widget->resize(x (), y (), SCROLLBAR_THICKNESS, SCROLLBAR_THICKNESS); + + switch (gadgetOrientation [visibility]) { + case GADGET_VERTICAL: + Y -= SCROLLBAR_THICKNESS; + break; + + case GADGET_HORIZONTAL: + X -= SCROLLBAR_THICKNESS; + break; + } + } +} + +void FltkViewport::adjustScrollbarValues () +{ + hscrollbar->value (scrollX, hscrollbar->w (), 0, canvasWidth); + vscrollbar->value (scrollY, vscrollbar->h (), 0, canvasHeight); +} + +void FltkViewport::hscrollbarChanged () +{ + scroll (hscrollbar->value () - scrollX, 0); +} + +void FltkViewport::vscrollbarChanged () +{ + scroll (0, vscrollbar->value () - scrollY); +} + +void FltkViewport::vscrollbarCallback (Fl_Widget *vscrollbar,void *viewportPtr) +{ + ((FltkViewport*)viewportPtr)->vscrollbarChanged (); +} + +void FltkViewport::hscrollbarCallback (Fl_Widget *hscrollbar,void *viewportPtr) +{ + ((FltkViewport*)viewportPtr)->hscrollbarChanged (); +} + +// ---------------------------------------------------------------------- + +void FltkViewport::resize(int X, int Y, int W, int H) +{ + bool dimension_changed = W != w() || H != h(); + + Fl_Group::resize(X, Y, W, H); + if (dimension_changed) { + theLayout->viewportSizeChanged (this, W, H); + adjustScrollbarsAndGadgetsAllocation (); + } +} + +void FltkViewport::draw_area (void *data, int x, int y, int w, int h) +{ + FltkViewport *vp = (FltkViewport*) data; + fl_push_clip(x, y, w, h); + + vp->FltkWidgetView::draw (); + + for (Iterator <TypedPointer < Fl_Widget> > it = vp->gadgets->iterator(); + it.hasNext (); ) { + Fl_Widget *widget = it.getNext()->getTypedValue (); + vp->draw_child (*widget); + } + + fl_pop_clip(); + +} + +void FltkViewport::draw () +{ + int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + int d = damage(); + + if (d & FL_DAMAGE_SCROLL) { + clear_damage (FL_DAMAGE_SCROLL); + fl_scroll(x(), y(), w() - hdiff, h() - vdiff, + -scrollDX, -scrollDY, draw_area, this); + clear_damage (d & ~FL_DAMAGE_SCROLL); + } + + if (d) { + draw_area(this, x(), y(), w () - hdiff, h () - vdiff); + + if (d == FL_DAMAGE_ALL || hscrollbar->damage ()) + draw_child (*hscrollbar); + if (d == FL_DAMAGE_ALL || vscrollbar->damage ()) + draw_child (*vscrollbar); + + if (d == FL_DAMAGE_ALL && hdiff && vdiff) { + fl_color(FL_BACKGROUND_COLOR); + fl_rectf(x()+w()-hdiff, y()+h()-vdiff, hdiff, vdiff); + } + } + + scrollDX = 0; + scrollDY = 0; +} + +int FltkViewport::handle (int event) +{ + _MSG("FltkViewport::handle %s\n", fl_eventnames[event]); + + switch(event) { + case FL_KEYBOARD: + /* When the viewport has focus (and not one of its children), FLTK + * sends the event here. Returning zero tells FLTK to resend the + * event as SHORTCUT, which we finally route to the parent. */ + + /* As we don't know the exact keybindings set by the user, we ask for + * all of them (except for the minimum needed to keep form navigation).*/ + if (Fl::event_key() != FL_Tab || Fl::event_ctrl()) + return 0; + break; + + case FL_SHORTCUT: + /* send it to the parent (UI) */ + return 0; + + case FL_FOCUS: + /** \bug Draw focus box. */ + break; + + case FL_UNFOCUS: + /** \bug Undraw focus box. */ + break; + + case FL_PUSH: + if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) { + if (vscrollbar->handle(event)) + verScrolling = 1; + } else if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) { + if (hscrollbar->handle(event)) + horScrolling = 1; + } else if (FltkWidgetView::handle(event) == 0 && + Fl::event_button() == FL_MIDDLE_MOUSE) { + if (!hasDragScroll) { + /* let the parent widget handle it... */ + return 0; + } else { + /* receive FL_DRAG and FL_RELEASE */ + dragScrolling = 1; + dragX = Fl::event_x(); + dragY = Fl::event_y(); + setCursor (core::style::CURSOR_MOVE); + } + } + return 1; + break; + + case FL_DRAG: + if (Fl::event_inside(this)) + Fl::remove_timeout(selectionScroll); + if (dragScrolling) { + scroll(dragX - Fl::event_x(), dragY - Fl::event_y()); + dragX = Fl::event_x(); + dragY = Fl::event_y(); + return 1; + } else if (verScrolling) { + vscrollbar->handle(event); + return 1; + } else if (horScrolling) { + hscrollbar->handle(event); + return 1; + } else if (!Fl::event_inside(this)) { + mouse_x = Fl::event_x(); + mouse_y = Fl::event_y(); + if (!Fl::has_timeout(selectionScroll, this)) + Fl::add_timeout(0.025, selectionScroll, this); + } + break; + + case FL_MOUSEWHEEL: + return (Fl::event_dx() ? hscrollbar : vscrollbar)->handle(event); + break; + + case FL_RELEASE: + Fl::remove_timeout(selectionScroll); + if (Fl::event_button() == FL_MIDDLE_MOUSE) { + setCursor (core::style::CURSOR_DEFAULT); + } else if (verScrolling) { + vscrollbar->handle(event); + } else if (horScrolling) { + hscrollbar->handle(event); + } + horScrolling = verScrolling = dragScrolling = 0; + break; + + case FL_ENTER: + /* could be the result of, e.g., closing another window. */ + mouse_x = Fl::event_x(); + mouse_y = Fl::event_y(); + positionChanged(); + break; + + case FL_LEAVE: + mouse_x = mouse_y = -1; + break; + } + + return FltkWidgetView::handle (event); +} + +// ---------------------------------------------------------------------- + +void FltkViewport::setCanvasSize (int width, int ascent, int descent) +{ + FltkWidgetView::setCanvasSize (width, ascent, descent); + adjustScrollbarValues (); +} + +/* + * This is used to simulate mouse motion (e.g., when scrolling). + */ +void FltkViewport::positionChanged () +{ + if (!dragScrolling && mouse_x >= x() && mouse_x < x()+w() && mouse_y >= y() + && mouse_y < y()+h()) + (void)theLayout->motionNotify (this, + translateViewXToCanvasX (mouse_x), + translateViewYToCanvasY (mouse_y), + (core::ButtonState)0); +} + +/* + * For scrollbars, this currently sets the same step to both vertical and + * horizontal. It may be differentiated if necessary. + */ +void FltkViewport::setScrollStep(int step) +{ + vscrollbar->linesize(step); + hscrollbar->linesize(step); +} + +bool FltkViewport::usesViewport () +{ + return true; +} + +int FltkViewport::getHScrollbarThickness () +{ + return SCROLLBAR_THICKNESS; +} + +int FltkViewport::getVScrollbarThickness () +{ + return SCROLLBAR_THICKNESS; +} + +void FltkViewport::scrollTo (int x, int y) +{ + int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0; + + x = misc::min (x, canvasWidth - w() + hdiff); + x = misc::max (x, 0); + + y = misc::min (y, canvasHeight - h() + vdiff); + y = misc::max (y, 0); + + if (x == scrollX && y == scrollY) { + return; + } + + /* multiple calls to scroll can happen before a redraw occurs. + * scrollDX and scrollDY can therefore be non-zero here. + */ + updateCanvasWidgets (x - scrollX, y - scrollY); + scrollDX += x - scrollX; + scrollDY += y - scrollY; + + scrollX = x; + scrollY = y; + + adjustScrollbarValues (); + damage(FL_DAMAGE_SCROLL); + theLayout->scrollPosChanged (this, scrollX, scrollY); + positionChanged(); +} + +void FltkViewport::scroll (int dx, int dy) +{ + scrollTo (scrollX + dx, scrollY + dy); +} + +void FltkViewport::scroll (core::ScrollCommand cmd) +{ + if (cmd == core::SCREEN_UP_CMD) { + scroll (0, -h () + vscrollbar->linesize ()); + } else if (cmd == core::SCREEN_DOWN_CMD) { + scroll (0, h () - vscrollbar->linesize ()); + } else if (cmd == core::SCREEN_LEFT_CMD) { + scroll (-w() + hscrollbar->linesize (), 0); + } else if (cmd == core::SCREEN_RIGHT_CMD) { + scroll (w() - hscrollbar->linesize (), 0); + } else if (cmd == core::LINE_UP_CMD) { + scroll (0, (int) -vscrollbar->linesize ()); + } else if (cmd == core::LINE_DOWN_CMD) { + scroll (0, (int) vscrollbar->linesize ()); + } else if (cmd == core::LEFT_CMD) { + scroll ((int) -hscrollbar->linesize (), 0); + } else if (cmd == core::RIGHT_CMD) { + scroll ((int) hscrollbar->linesize (), 0); + } else if (cmd == core::TOP_CMD) { + scrollTo (scrollX, 0); + } else if (cmd == core::BOTTOM_CMD) { + scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */ + } +} + +/* + * Scrolling in response to selection where the cursor is outside the view. + */ +void FltkViewport::selectionScroll () +{ + int distance; + int dx = 0, dy = 0; + + if ((distance = x() - mouse_x) > 0) + dx = -distance * hscrollbar->linesize () / 48 - 1; + else if ((distance = mouse_x - (x() + w())) > 0) + dx = distance * hscrollbar->linesize () / 48 + 1; + if ((distance = y() - mouse_y) > 0) + dy = -distance * vscrollbar->linesize () / 48 - 1; + else if ((distance = mouse_y - (y() + h())) > 0) + dy = distance * vscrollbar->linesize () / 48 + 1; + + scroll (dx, dy); +} + +void FltkViewport::selectionScroll (void *data) +{ + ((FltkViewport *)data)->selectionScroll (); + Fl::repeat_timeout(0.025, selectionScroll, data); +} + +void FltkViewport::setViewportSize (int width, int height, + int hScrollbarThickness, + int vScrollbarThickness) +{ + int adjustReq = + (hscrollbar->visible() ? !hScrollbarThickness : hScrollbarThickness) || + (vscrollbar->visible() ? !vScrollbarThickness : vScrollbarThickness); + + _MSG("FltkViewport::setViewportSize old_w,old_h=%dx%d -> w,h=%dx%d\n" + "\t hThick=%d hVis=%d, vThick=%d vVis=%d, adjustReq=%d\n", + w(),h(),width,height, + hScrollbarThickness,hscrollbar->visible(), + vScrollbarThickness,vscrollbar->visible(), adjustReq); + + (hScrollbarThickness > 0) ? hscrollbar->show () : hscrollbar->hide (); + (vScrollbarThickness > 0) ? vscrollbar->show () : vscrollbar->hide (); + + /* If no scrollbar, go to the beginning */ + scroll(hScrollbarThickness ? 0 : -scrollX, + vScrollbarThickness ? 0 : -scrollY); + + /* Adjust when scrollbar visibility changes */ + if (adjustReq) + adjustScrollbarsAndGadgetsAllocation (); +} + +void FltkViewport::updateCanvasWidgets (int dx, int dy) +{ + // scroll all child widgets except scroll bars + for (int i = children () - 1; i > 0; i--) { + Fl_Widget *widget = child (i); + + if (widget == hscrollbar || widget == vscrollbar) + continue; + + widget->position(widget->x () - dx, widget->y () - dy); + } +} + +int FltkViewport::translateViewXToCanvasX (int X) +{ + return X - x () + scrollX; +} + +int FltkViewport::translateViewYToCanvasY (int Y) +{ + return Y - y () + scrollY; +} + +int FltkViewport::translateCanvasXToViewX (int X) +{ + return X + x () - scrollX; +} + +int FltkViewport::translateCanvasYToViewY (int Y) +{ + return Y + y () - scrollY; +} + +// ---------------------------------------------------------------------- + +void FltkViewport::setGadgetOrientation (bool hscrollbarVisible, + bool vscrollbarVisible, + FltkViewport::GadgetOrientation + gadgetOrientation) +{ + this->gadgetOrientation[(hscrollbarVisible ? 0 : 1) | + (vscrollbarVisible ? 0 : 2)] = gadgetOrientation; + adjustScrollbarsAndGadgetsAllocation (); +} + +void FltkViewport::addGadget (Fl_Widget *gadget) +{ + /** \bug Reparent? */ + + gadgets->append (new TypedPointer < Fl_Widget> (gadget)); + adjustScrollbarsAndGadgetsAllocation (); +} + + +} // namespace fltk +} // namespace dw diff --git a/dw/fltkviewport.hh b/dw/fltkviewport.hh new file mode 100644 index 0000000..1569a7d --- /dev/null +++ b/dw/fltkviewport.hh @@ -0,0 +1,84 @@ +#ifndef __DW_FLTKVIEWPORT_HH__ +#define __DW_FLTKVIEWPORT_HH__ + +#include <FL/Fl_Group.H> +#include <FL/Fl_Scrollbar.H> + +#include "core.hh" +#include "fltkcore.hh" +#include "fltkviewbase.hh" + +namespace dw { +namespace fltk { + +class FltkViewport: public FltkWidgetView +{ +public: + enum GadgetOrientation { GADGET_VERTICAL, GADGET_HORIZONTAL }; + +private: + enum { SCROLLBAR_THICKNESS = 15 }; + + int scrollX, scrollY; + int scrollDX, scrollDY; + int hasDragScroll, dragScrolling, dragX, dragY; + int horScrolling, verScrolling; + + Fl_Scrollbar *vscrollbar, *hscrollbar; + + GadgetOrientation gadgetOrientation[4]; + lout::container::typed::List <lout::object::TypedPointer < Fl_Widget> > + *gadgets; + + void adjustScrollbarsAndGadgetsAllocation (); + void adjustScrollbarValues (); + void hscrollbarChanged (); + void vscrollbarChanged (); + void positionChanged (); + + static void hscrollbarCallback (Fl_Widget *hscrollbar, void *viewportPtr); + static void vscrollbarCallback (Fl_Widget *vscrollbar, void *viewportPtr); + + void selectionScroll(); + static void selectionScroll(void *vport); + + void updateCanvasWidgets (int oldScrollX, int oldScrollY); + static void draw_area (void *data, int x, int y, int w, int h); + +protected: + int translateViewXToCanvasX (int x); + int translateViewYToCanvasY (int y); + int translateCanvasXToViewX (int x); + int translateCanvasYToViewY (int y); + +public: + FltkViewport (int x, int y, int w, int h, const char *label = 0); + ~FltkViewport (); + + void resize(int x, int y, int w, int h); + void draw (); + int handle (int event); + + void setCanvasSize (int width, int ascent, int descent); + + bool usesViewport (); + int getHScrollbarThickness (); + int getVScrollbarThickness (); + void scroll(int dx, int dy); + void scroll(dw::core::ScrollCommand cmd); + void scrollTo (int x, int y); + void setViewportSize (int width, int height, + int hScrollbarThickness, int vScrollbarThickness); + void setScrollStep(int step); + + void setGadgetOrientation (bool hscrollbarVisible, bool vscrollbarVisible, + GadgetOrientation gadgetOrientation); + void setDragScroll (bool enable) { hasDragScroll = enable ? 1 : 0; } + void addGadget (Fl_Widget *gadget); +}; + +} // namespace fltk +} // namespace dw + +#endif // __DW_FLTKVIEWPORT_HH__ + diff --git a/dw/imgbuf.hh b/dw/imgbuf.hh new file mode 100644 index 0000000..f9870bc --- /dev/null +++ b/dw/imgbuf.hh @@ -0,0 +1,229 @@ +#ifndef __DW_IMGBUF_HH__ +#define __DW_IMGBUF_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/debug.hh" + +namespace dw { +namespace core { + +/** + * \brief The platform independent interface for image buffers. + * + * %Image buffers depend on the platform (see \ref dw-images-and-backgrounds), + * but have this general, platform independent interface. The purpose of + * an image buffer is + * + * <ol> + * <li> storing the image data, + * <li> handling scaled versions of this buffer, and + * <li> drawing. + * </ol> + * + * The latter must be done independently from the window. + * + * <h3>Creating</h3> + * + * %Image buffers are created by calling dw::core::Platform::createImgbuf. + * + * <h3>Storing %Image Data</h3> + * + * dw::core::Imgbuf supports five image types, which are listed in the table + * below. The representation defines, how the colors are stored within + * the data, which is passed to dw::core::Imgbuf::copyRow. + * + * <table> + * <tr><th>Type (dw::core::Imgbuf::Type) <th>Bytes per + * Pixel <th>Representation + * <tr><td>dw::core::Imgbuf::RGB <td>3 <td>red, green, blue + * <tr><td>dw::core::Imgbuf::RGBA <td>4 <td>red, green, blue, alpha + * <tr><td>dw::core::Imgbuf::GRAY <td>1 <td>gray value + * <tr><td>dw::core::Imgbuf::INDEXED <td>1 <td>index to colormap + * <tr><td>dw::core::Imgbuf::INDEXED_ALPHA <td>1 <td>index to colormap + * </table> + * + * The last two types need a colormap, which is set by + * dw::core::Imgbuf::setCMap, which must be called before + * dw::core::Imgbuf::copyRow. This function expects the colors as 32 bit + * unsigned integers, which have the format 0xrrbbgg (for indexed + * images), or 0xaarrggbb (for indexed alpha), respectively. + * + * + * <h3>Scaling</h3> + * + * The buffer with the original size, which was created by + * dw::core::Platform::createImgbuf, is called root buffer. Imgbuf provides + * the ability to scale buffers. Generally, both root buffers, as well as + * scaled buffers, may be shared, memory management is done by reference + * counters. + * + * Via dw::core::Imgbuf::getScaledBuf, you can retrieve a scaled buffer. + * Generally, something like this must work always, in an efficient way: + * + * \code + * dw::core::Imgbuf *curBuf, *oldBuf; + * int width, heigt, + * // ... + * oldBuf = curBuf; + * curBuf = oldBuf->getScaledBuf(oldBuf, width, height); + * oldBuf->unref(); + * \endcode + * + * \em oldBuf may both be a root buffer, or a scaled buffer. + * + * The root buffer keeps a list of all children, and all methods + * operating on the image data (dw::core::Imgbuf::copyRow and + * dw::core::Imgbuf::setCMap) are delegated to the scaled buffers, when + * processed, and inherited, when a new scaled buffer is created. This + * means, that they must only be performed for the root buffer. + * + * A possible implementation could be (dw::fltk::FltkImgbuf does it this way): + * + * <ul> + * <li> If the method is called with an already scaled image buffer, this is + * delegated to the root buffer. + * + * <li> If the given size is the original size, the root buffer is + * returned, with an increased reference counter. + * + * <li> Otherwise, if this buffer has already been scaled to the given + * size, return this scaled buffer, with an increased reference + * counter. + * + * <li> Otherwise, return a new scaled buffer with reference counter 1. + * </ul> + * + * Special care is to be taken, when the root buffer is not used anymore, + * i.e. after dw::core::Imgbuf::unref the reference counter is 0, but there + * are still scaled buffers. Since all methods operating on the image data + * (dw::core::Imgbuf::copyRow and dw::core::Imgbuf::setCMap) are called for + * the root buffer, the root buffer is still needed, and so must not be + * deleted at this point. This is, how dw::fltk::FltkImgbuf solves this + * problem: + * + * <ul> + * <li> dw::fltk::FltkImgbuf::unref does, for root buffers, check, not only + * whether dw::fltk::FltkImgbuf::refCount is 0, but also, whether + * there are children left. When the latter is the case, the buffer + * is not deleted. + * + * <li> There is an additional check in dw::fltk::FltkImgbuf::detachScaledBuf, + * which deals with the case, that dw::fltk::FltkImgbuf::refCount is 0, + * and the last scaled buffer is removed. + * </ul> + * + * In the following example: + * + * \code + * dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform (); + * dw::core::Layout *layout = new dw::core::Layout (platform); + * + * dw::core::Imgbuf *rootbuf = + * layout->createImgbuf (dw::core::Imgbuf::RGB, 100, 100); + * dw::core::Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50); + * rootbuf->unref (); + * scaledbuf->unref (); + * \endcode + * + * the root buffer is not deleted, when dw::core::Imgbuf::unref is called, + * since a scaled buffer is left. After calling dw::core::Imgbuf::unref for + * the scaled buffer, it is deleted, and after it, the root buffer. + * + * <h3>Drawing</h3> + * + * dw::core::Imgbuf provides no methods for drawing, instead, this is + * done by the views (implementation of dw::core::View). + * + * There are two situations, when drawing is necessary: + * + * <ol> + * <li> To react on expose events, the function dw::core::View::drawImage + * should be used, with the following parameters: + * <ul> + * <li> of course, the image buffer, + * <li> where the root of the image would be displayed (as \em xRoot + * and \em yRoot), and + * <li> the region within the image, which should be displayed (\em x, + * \em y, \em width, \em height). + * </ul> + * + * <li> When a row has been copied, it has to be drawn. To determine the + * area, which has to be drawn, the dw::core::Imgbuf::getRowArea + * should be used. The result can then passed + * to dw::core::View::drawImage. + * </ol> + * + * \sa \ref dw-images-and-backgrounds + */ +class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject +{ +public: + enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA }; + + inline Imgbuf () { + DBG_OBJ_CREATE ("dw::core::Imgbuf"); + DBG_OBJ_BASECLASS (lout::object::Object); + DBG_OBJ_BASECLASS (lout::signal::ObservedObject); + } + + /* + * Methods called from the image decoding + */ + + virtual void setCMap (int *colors, int num_colors) = 0; + virtual void copyRow (int row, const byte *data) = 0; + virtual void newScan () = 0; + + /* + * Methods called from dw::Image + */ + + virtual Imgbuf* getScaledBuf (int width, int height) = 0; + virtual void getRowArea (int row, dw::core::Rectangle *area) = 0; + virtual int getRootWidth () = 0; + virtual int getRootHeight () = 0; + + + /** + * Creates an image buffer with same parameters (type, gamma etc.) + * except size. + */ + virtual Imgbuf *createSimilarBuf (int width, int height) = 0; + + /** + * Copies another image buffer into this image buffer. + */ + virtual void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc) = 0; + + /* + * Reference counting. + */ + + virtual void ref () = 0; + virtual void unref () = 0; + + /** + * \todo Comment + */ + virtual bool lastReference () = 0; + + + /** + * \todo Comment + */ + virtual void setDeleteOnUnref (bool deleteOnUnref) = 0; + + /** + * \todo Comment + */ + virtual bool isReferred () = 0; +}; + +} // namespace core +} // namespace dw + +#endif // __DW_IMGBUF_HH__ diff --git a/dw/imgrenderer.cc b/dw/imgrenderer.cc new file mode 100644 index 0000000..1868122 --- /dev/null +++ b/dw/imgrenderer.cc @@ -0,0 +1,77 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2013 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "core.hh" + +namespace dw { +namespace core { + +using namespace lout::container; +using namespace lout::object; + +void ImgRendererDist::setBuffer (core::Imgbuf *buffer, bool resize) +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->setBuffer (buffer, resize); + } +} + +void ImgRendererDist::drawRow (int row) +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->drawRow (row); + } +} + + +void ImgRendererDist::finish () +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->finish (); + } +} + +void ImgRendererDist::fatal () +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->fatal (); + } +} + + +} // namespace core +} // namespace dw diff --git a/dw/imgrenderer.hh b/dw/imgrenderer.hh new file mode 100644 index 0000000..e3b5e95 --- /dev/null +++ b/dw/imgrenderer.hh @@ -0,0 +1,87 @@ +#ifndef __DW_IMGRENDERER_HH__ +#define __DW_IMGRENDERER_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief ... + * + * \sa \ref dw-images-and-backgrounds + */ +class ImgRenderer +{ +public: + virtual ~ImgRenderer () { } + + /** + * \brief Called, when an image buffer is attached. + * + * This is typically the case when all meta data (size, depth) has been read. + */ + virtual void setBuffer (core::Imgbuf *buffer, bool resize = false) = 0; + + /** + * \brief Called, when data from a row is available and has been copied into + * the image buffer. + * + * The implementation will typically queue the respective area for drawing. + */ + virtual void drawRow (int row) = 0; + + /** + * \brief Called, when all image data has been retrieved. + * + * The implementation may use this instead of "drawRow" for drawing, to + * limit the number of draws. + */ + virtual void finish () = 0; + + /** + * \brief Called, when there are problems with the retrieval of image data. + * + * The implementation may use this to indicate an error. + */ + virtual void fatal () = 0; +}; + +/** + * \brief Implementation of ImgRenderer, which distributes all calls + * to a set of other implementations of ImgRenderer. + * + * The order of the call children is not defined, especially not + * identical to the order in which they have been added. + */ +class ImgRendererDist: public ImgRenderer +{ + lout::container::typed::HashSet <lout::object::TypedPointer <ImgRenderer> > + *children; + +public: + inline ImgRendererDist () + { children = new lout::container::typed::HashSet + <lout::object::TypedPointer <ImgRenderer> > (true); } + ~ImgRendererDist () { delete children; } + + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + + void put (ImgRenderer *child) + { children->put (new lout::object::TypedPointer <ImgRenderer> (child)); } + void remove (ImgRenderer *child) + { lout::object::TypedPointer <ImgRenderer> tp (child); + children->remove (&tp); } +}; + +} // namespace core +} // namespace dw + +#endif // __DW_IMGRENDERER_HH__ + + diff --git a/dw/iterator.cc b/dw/iterator.cc new file mode 100644 index 0000000..de934d0 --- /dev/null +++ b/dw/iterator.cc @@ -0,0 +1,920 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include <limits.h> + +using namespace lout; + +namespace dw { +namespace core { + +// -------------- +// Iterator +// -------------- + +Iterator::Iterator(Widget *widget, Content::Type mask, bool atEnd) +{ + this->widget = widget; + this->mask = mask; +} + +Iterator::Iterator(Iterator &it): object::Comparable () +{ + widget = it.widget; + content = it.content; +} + +Iterator::~Iterator() +{ +} + +bool Iterator::equals (Object *other) +{ + Iterator *otherIt = (Iterator*)other; + return + this == otherIt || + (getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0); +} + +void Iterator::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("{ widget = "); + //widget->intoStringBuffer (sb); + sb->appendPointer (widget); + sb->append (" ("); + sb->append (widget->getClassName()); + sb->append (")>"); + + sb->append (", mask = "); + Content::maskIntoStringBuffer (mask, sb); + + sb->append (", content = "); + Content::intoStringBuffer (&content, sb); + + sb->append (" }"); +} + +/** + * \brief Delete the iterator. + * + * The destructor is hidden, implementations may use optimizations for + * the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.) + */ +void Iterator::unref () +{ + delete this; +} + +/** + * \brief Scrolls the viewport, so that the region between \em it1 and + * \em it2 is seen, according to \em hpos and \em vpos. + * + * The parameters \em start and \em end have the same meaning as in + * dw::core::Iterator::getAllocation, \em start refers + * to \em it1, while \em end rerers to \em it2. + * + * If \em it1 and \em it2 point to the same location (see code), only + * \em it1 is regarded, and both belowstart and belowend refer to it. + */ +void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, + HPosition hpos, VPosition vpos) +{ + Allocation alloc1, alloc2, alloc; + int x1, x2, y1, y2; + DeepIterator *eit1, *eit2, *eit3; + int curStart, curEnd, cmp; + bool atStart; + + if (it1->equals(it2)) { + it1->getAllocation (start, end, &alloc); + it1->getWidget()->getLayout()->scrollTo (hpos, vpos, alloc.x, alloc.y, + alloc.width, + alloc.ascent + alloc.descent); + } else { + // First, determine the rectangle all iterators from it1 and it2 + // allocate, i.e. the smallest rectangle containing all allocations of + // these iterators. + eit1 = new DeepIterator (it1); + eit2 = new DeepIterator (it2); + + x1 = INT_MAX; + x2 = INT_MIN; + y1 = INT_MAX; + y2 = INT_MIN; + + for (eit3 = (DeepIterator*)eit1->clone (), atStart = true; + (cmp = eit3->compareTo (eit2)) <= 0; + eit3->next (), atStart = false) { + if (atStart) + curStart = start; + else + curStart = 0; + + if (cmp == 0) + curEnd = end; + else + curEnd = INT_MAX; + + eit3->getAllocation (curStart, curEnd, &alloc); + x1 = misc::min (x1, alloc.x); + x2 = misc::max (x2, alloc.x + alloc.width); + y1 = misc::min (y1, alloc.y); + y2 = misc::max (y2, alloc.y + alloc.ascent + alloc.descent); + } + + delete eit3; + delete eit2; + delete eit1; + + it1->getAllocation (start, INT_MAX, &alloc1); + it2->getAllocation (0, end, &alloc2); + + if (alloc1.x > alloc2.x) { + // + // This is due to a line break within the region. When the line is + // longer than the viewport, and the region is actually quite short, + // the user would not see anything of the region, as in this figure + // (with region marked as "#"): + // + // +----------+ ,-- alloc1 + // | | V + // | | ### ### + // ### ### | | + // ^ | | <-- viewport + // | +----------+ + // `-- alloc2 + // |----------------------------| + // width + // + // Therefore, we make the region smaller, so that the region will be + // displayed like this: + // + // ,-- alloc1 + // +----|-----+ + // | V | + // | ### ###| + // ### ### | | + // ^ | | <-- viewport + // `-- alloc2 +----------+ + // |----------| + // width + // + + /** \todo Changes in the viewport size, until the idle function is + * called, are not regarded. */ + + if (it1->getWidget()->getLayout()->getUsesViewport() && + x2 - x1 > it1->getWidget()->getLayout()->getWidthViewport()) { + x1 = x2 - it1->getWidget()->getLayout()->getWidthViewport(); + x2 = x1 + it1->getWidget()->getLayout()->getWidthViewport(); + } + } + + if (alloc1.y > alloc2.y) { + // This is similar to the case above, e.g. if the region ends in + // another table column. + if (it1->getWidget()->getLayout()->getUsesViewport() && + y2 - y1 > it1->getWidget()->getLayout()->getHeightViewport()) { + y1 = y2 - it1->getWidget()->getLayout()->getHeightViewport(); + y2 = y1 + it1->getWidget()->getLayout()->getHeightViewport(); + } + } + + it1->getWidget()->getLayout()->scrollTo (hpos, vpos, + x1, y1, x2 - x1, y2 - y1); + } +} + + +void Iterator::print () +{ + misc::StringBuffer sb; + intoStringBuffer (&sb); + printf ("%s", sb.getChars ()); +} + +// ------------------- +// EmptyIterator +// ------------------- + +EmptyIterator::EmptyIterator (Widget *widget, Content::Type mask, bool atEnd): + Iterator (widget, mask, atEnd) +{ + this->content.type = (atEnd ? Content::END : Content::START); +} + +EmptyIterator::EmptyIterator (EmptyIterator &it): Iterator (it) +{ +} + +object::Object *EmptyIterator::clone () +{ + return new EmptyIterator (*this); +} + +int EmptyIterator::compareTo (object::Comparable *other) +{ + EmptyIterator *otherIt = (EmptyIterator*)other; + + if (content.type == otherIt->content.type) + return 0; + else if (content.type == Content::START) + return -1; + else + return +1; +} + +bool EmptyIterator::next () +{ + content.type = Content::END; + return false; +} + +bool EmptyIterator::prev () +{ + content.type = Content::START; + return false; +} + +void EmptyIterator::highlight (int start, int end, HighlightLayer layer) +{ +} + +void EmptyIterator::unhighlight (int direction, HighlightLayer layer) +{ +} + +void EmptyIterator::getAllocation (int start, int end, Allocation *allocation) +{ +} + +// ------------------ +// TextIterator +// ------------------ + +TextIterator::TextIterator (Widget *widget, Content::Type mask, bool atEnd, + const char *text): Iterator (widget, mask, atEnd) +{ + this->content.type = (atEnd ? Content::END : Content::START); + this->text = (mask & Content::TEXT) ? text : NULL; +} + +TextIterator::TextIterator (TextIterator &it): Iterator (it) +{ + text = it.text; +} + +int TextIterator::compareTo (object::Comparable *other) +{ + TextIterator *otherIt = (TextIterator*)other; + + if (content.type == otherIt->content.type) + return 0; + + switch (content.type) { + case Content::START: + return -1; + + case Content::TEXT: + if (otherIt->content.type == Content::START) + return +1; + else + return -1; + + case Content::END: + return +1; + + default: + misc::assertNotReached(); + return 0; + } +} + +bool TextIterator::next () +{ + if (content.type == Content::START && text != NULL) { + content.type = Content::TEXT; + content.text = text; + return true; + } else { + content.type = Content::END; + return false; + } +} + +bool TextIterator::prev () +{ + if (content.type == Content::END && text != NULL) { + content.type = Content::TEXT; + content.text = text; + return true; + } else { + content.type = Content::START; + return false; + } +} + +void TextIterator::getAllocation (int start, int end, Allocation *allocation) +{ + // Return the allocation of the widget. + *allocation = *(getWidget()->getAllocation ()); +} + +// ------------------ +// DeepIterator +// ------------------ + +DeepIterator::Stack::~Stack () +{ + for (int i = 0; i < size (); i++) + get(i)->unref (); +} + +/* + * The following two methods are used by dw::core::DeepIterator::DeepIterator, + * when the passed dw::core::Iterator points to a widget. Since a + * dw::core::DeepIterator never returns a widget, the dw::core::Iterator has + * to be corrected, by searching for the next content downwards (within the + * widget pointed to), forwards, and backwards (in the traversed tree). + */ + +/* + * Search downwards. If fromEnd is true, start search at the end, + * otherwise at the beginning. + */ +Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, + bool fromEnd) +{ + Iterator *it2, *it3; + + //DEBUG_MSG (1, "%*smoving down (%swards) from %s\n", + // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); + + assert (it->getContent()->type & Content::ANY_WIDGET); + it2 = it->getContent()->widget->iterator (mask, fromEnd); + + if (it2 == NULL) { + // Moving downwards failed. + //DEBUG_MSG (1, "%*smoving down failed\n", indent, ""); + return NULL; + } + + while (fromEnd ? it2->prev () : it2->next ()) { + //DEBUG_MSG (1, "%*sexamining %s\n", + // indent, "", a_Dw_iterator_text (it2)); + + if (it2->getContent()->type & Content::ANY_WIDGET) { + // Another widget. Search in it downwards. + it3 = searchDownward (it2, mask, fromEnd); + if (it3 != NULL) { + it2->unref (); + return it3; + } + // Else continue in this widget. + } else { + // Success! + //DEBUG_MSG (1, "%*smoving down succeeded: %s\n", + // indent, "", a_Dw_iterator_text (it2)); + return it2; + } + } + + // Nothing found. + it2->unref (); + //DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, ""); + return NULL; +} + +/* + * Search sidewards. fromEnd specifies the direction, false means forwards, + * true means backwards. + */ +Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, + bool fromEnd) +{ + Iterator *it2, *it3; + + //DEBUG_MSG (1, "%*smoving %swards from %s\n", + // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); + + assert (it->getContent()->type & Content::ANY_WIDGET); + it2 = it->cloneIterator (); + + while (fromEnd ? it2->prev () : it2->next ()) { + if (it2->getContent()->type & Content::ANY_WIDGET) { + // Search downwards in this widget. + it3 = searchDownward (it2, mask, fromEnd); + if (it3 != NULL) { + it2->unref (); + //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it3)); + return it3; + } + // Else continue in this widget. + } else { + // Success! + // DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it2)); + return it2; + } + } + + /* Nothing found, go upwards in the tree (if possible). */ + it2->unref (); + Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask()); + if (respParent) { + it2 = respParent->iterator (mask, false); + while (true) { + if (!it2->next ()) + misc::assertNotReached (); + + if (it2->getContent()->type & Content::ANY_WIDGET && + it2->getContent()->widget == it->getWidget ()) { + it3 = searchSideward (it2, mask, fromEnd); + it2->unref (); + //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it3)); + return it3; + } + } + } + + // Nothing found at all. + // DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n", + // indent, "", from_end ? "back" : "for"); + return NULL; +} + +Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask) +{ + // Return, depending on which is requested indirectly (follow + // references or containments) the parent (container) or the + // generator. At this point, the type of the parent/generator is + // not known (since the parent/generator is not known), so we have + // to examine the mask. This is the reason why only one of + // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGenerator() : widget->getParent(); +} + +int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask) +{ + // Similar to getRespectiveParent. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGeneratorLevel() : widget->getLevel(); +} + +/** + * \brief Create a new deep iterator from an existing dw::core::Iterator. + * + * The content of the return value will be the content of \em it. If within + * the widget tree, there is no non-widget content, the resulting deep + * iterator is empty (denoted by dw::core::DeepIterator::stack == NULL). + * + * Notes: + * + * <ol> + * <li> The mask of \em i" must include DW_CONTENT_WIDGET, but + * dw::core::DeepIterator::next will never return widgets. + * </ol> + */ +DeepIterator::DeepIterator (Iterator *it) +{ + //printf ("Starting creating DeepIterator %p ...\n", this); + //printf ("Initial iterator: "); + //it->print (); + //printf ("\n"); + + // Widgets out of flow are either followed widtin containers, or + // generators. Both (and also nothing at all) is not allowed. See + // also comment in getRespectiveParent. + int oofMask = + it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF); + assert (oofMask == Content::WIDGET_OOF_CONT || + oofMask == Content::WIDGET_OOF_REF); + + //DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it)); + + // Clone input iterator, so the iterator passed as parameter + // remains untouched. + it = it->cloneIterator (); + this->mask = it->getMask (); + + hasContents = true; + + // If it points to a widget, find a near non-widget content, + // since an DeepIterator should never return widgets. + if (it->getContent()->type & Content::ANY_WIDGET) { + Iterator *it2; + + // The second argument of searchDownward is actually a matter of + // taste :-) + if ((it2 = searchDownward (it, mask, false)) || + (it2 = searchSideward (it, mask, false)) || + (it2 = searchSideward (it, mask, true))) { + it->unref (); + it = it2; + } else { + // This may happen, when a page does not contain any non-widget + // content. + //DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n"); + it->unref (); + hasContents = false; + } + } + + //DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it)); + + if (hasContents) { + // If this widget has parents, we must construct appropriate iterators. + // + // \todo There may be a faster way instead of iterating through the + // parent widgets. + + //printf ("Starting with: "); + //it->print (); + //printf ("\n"); + + // Construct the iterators. + int thisLevel = getRespectiveLevel (it->getWidget()), level; + Widget *w; + for (w = it->getWidget (), level = thisLevel; + getRespectiveParent (w) != NULL; + w = getRespectiveParent (w), level--) { + Iterator *it = getRespectiveParent(w)->iterator (mask, false); + + //printf (" parent: %s %p\n", w->getClassName (), w); + + stack.put (it, level - 1); + while (true) { + //printf (" "); + //it->print (); + //printf ("\n"); + + bool hasNext = it->next(); + assert (hasNext); + + if (it->getContent()->type & Content::ANY_WIDGET && + it->getContent()->widget == w) + break; + } + + //printf (" %d: ", level - 1); + //it->print (); + //printf ("\n"); + } + + stack.put (it, thisLevel); + content = *(it->getContent()); + } + + //printf ("... done creating DeepIterator %p.\n", this); +} + + +DeepIterator::~DeepIterator () +{ + //printf ("Deleting DeepIterator %p ...\n", this); +} + +object::Object *DeepIterator::clone () +{ + DeepIterator *it = new DeepIterator (); + + for (int i = 0; i < stack.size (); i++) + it->stack.put (stack.get(i)->cloneIterator (), i); + + it->mask = mask; + it->content = content; + it->hasContents = hasContents; + + return it; +} + +int DeepIterator::compareTo (object::Comparable *other) +{ + DeepIterator *otherDeepIterator = (DeepIterator*)other; + + //printf ("Compare: %s\n", stack.toString ()); + //printf (" to: %s\n", otherDeepIterator->stack.toString ()); + + // Search the highest level, where the widgets are the same. + int level = 0; + + // The Comparable interface does not define "uncomparable". Deep + // iterators are only comparable if they belong to the same widget + // tree, so have the same widget at the bottom at the + // stack. If this is not the case, we abort. + + assert (stack.size() > 0); + assert (otherDeepIterator->stack.size() > 0); + + //printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n", + // stack.get(0)->getWidget()->getClassName(), + // stack.get(0)->getWidget(), this, + // otherDeepIterator->stack.get(0)->getWidget()->getClassName(), + // otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator); + + assert (stack.get(0)->getWidget() + == otherDeepIterator->stack.get(level)->getWidget()); + + while (stack.get(level)->getWidget () + == otherDeepIterator->stack.get(level)->getWidget ()) { + if (level == stack.size() - 1 || + level == otherDeepIterator->stack.size() - 1) + break; + level++; + } + + //printf (" => level = %d (temorally)\n", level); + + while (stack.get(level)->getWidget () + != otherDeepIterator->stack.get(level)->getWidget ()) + level--; + + //printf (" => level = %d (finally)\n", level); + + return stack.get(level)->compareTo (otherDeepIterator->stack.get(level)); +} + +DeepIterator *DeepIterator::createVariant(Iterator *it) +{ + /** \todo Not yet implemented, and actually not yet needed very much. */ + return new DeepIterator (it); +} + +bool DeepIterator::isEmpty () { + return !hasContents; +} + +/** + * \brief Move iterator forward and store content it. + * + * Returns true on success. + */ +bool DeepIterator::next () +{ + Iterator *it = stack.getTop (); + + if (it->next ()) { + if (it->getContent()->type & Content::ANY_WIDGET) { + // Widget: new iterator on stack, to search in this widget. + stack.push (it->getContent()->widget->iterator (mask, false)); + return next (); + } else { + // Simply return the content of the iterartor. + content = *(it->getContent ()); + return true; + } + } else { + // No more data in the top-most widget. + if (stack.size () > 1) { + // Pop iterator from stack, and move to next item in the old one. + stack.pop (); + return next (); + } else { + // Stack is empty. + content.type = Content::END; + return false; + } + } +} + +/** + * \brief Move iterator backward and store content it. + * + * Returns true on success. + */ +bool DeepIterator::prev () +{ + Iterator *it = stack.getTop (); + + if (it->prev ()) { + if (it->getContent()->type & Content::ANY_WIDGET) { + // Widget: new iterator on stack, to search in this widget. + stack.push (it->getContent()->widget->iterator (mask, true)); + return prev (); + } else { + // Simply return the content of the iterartor. + content = *(it->getContent ()); + return true; + } + } else { + // No more data in the top-most widget. + if (stack.size () > 1) { + // Pop iterator from stack, and move to next item in the old one. + stack.pop (); + return prev (); + } else { + // Stack is empty. + content.type = Content::START; + return false; + } + } +} + +// ----------------- +// CharIterator +// ----------------- + +CharIterator::CharIterator () +{ + it = NULL; +} + +/** + * \brief ... + * + * If followReferences is true, only the reference are followed, when + * the container and generator for a widget is different. If false, + * only the container is followed. + */ +CharIterator::CharIterator (Widget *widget, bool followReferences) +{ + Iterator *i = + widget->iterator (Content::maskForSelection (followReferences), false); + it = new DeepIterator (i); + i->unref (); + ch = START; +} + +CharIterator::~CharIterator () +{ + if (it) + delete it; +} + +object::Object *CharIterator::clone() +{ + CharIterator *cloned = new CharIterator (); + cloned->it = it->cloneDeepIterator (); + cloned->ch = ch; + cloned->pos = pos; + return cloned; +} + +int CharIterator::compareTo(object::Comparable *other) +{ + CharIterator *otherIt = (CharIterator*)other; + int c = it->compareTo(otherIt->it); + if (c != 0) + return c; + else + return pos - otherIt->pos; +} + +bool CharIterator::next () +{ + if (ch == START || it->getContent()->type == Content::BREAK || + (it->getContent()->type == Content::TEXT && + it->getContent()->text[pos] == 0)) { + if (it->next()) { + if (it->getContent()->type == Content::BREAK) + ch = '\n'; + else { // if (it->getContent()->type == Content::TEXT) + pos = 0; + ch = it->getContent()->text[pos]; + if (ch == 0) + // should not happen, actually + return next (); + } + return true; + } + else { + ch = END; + return false; + } + } else if (ch == END) + return false; + else { + // at this point, it->getContent()->type == Content::TEXT + pos++; + ch = it->getContent()->text[pos]; + if (ch == 0) { + if (it->getContent()->space) { + ch = ' '; + } else { + return next (); + } + } + + return true; + } +} + +bool CharIterator::prev () +{ + if (ch == END || it->getContent()->type == Content::BREAK || + (it->getContent()->type == Content::TEXT && pos == 0)) { + if (it->prev()) { + if (it->getContent()->type == Content::BREAK) + ch = '\n'; + else { // if (it->getContent()->type == Content::TEXT) + if (it->getContent()->text[0] == 0) + return prev (); + else { + pos = strlen (it->getContent()->text); + if (it->getContent()->space) { + ch = ' '; + } else { + pos--; + ch = it->getContent()->text[pos]; + } + } + } + return true; + } + else { + ch = START; + return false; + } + } else if (ch == START) + return false; + else { + // at this point, it->getContent()->type == Content::TEXT + pos--; + ch = it->getContent()->text[pos]; + return true; + } +} + +void CharIterator::highlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer) +{ + if (it2->getChar () == CharIterator::END) + it2->prev (); + + if (it1->it->compareTo (it2->it) == 0) + // Only one content => highlight part of it. + it1->it->highlight (it1->pos, it2->pos, layer); + else { + DeepIterator *it = it1->it->cloneDeepIterator (); + int c; + bool start; + for (start = true; + (c = it->compareTo (it2->it)) <= 0; + it->next (), start = false) { + int endOfWord = + it->getContent()->type == Content::TEXT ? + strlen (it->getContent()->text) : 1; + if (start) // first iteration + it->highlight (it1->pos, endOfWord, layer); + else if (c == 0) // last iteration + it->highlight (0, it2->pos, layer); + else + it->highlight (0, endOfWord, layer); + } + delete it; + } +} + +void CharIterator::unhighlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer) +{ + if (it1->it->compareTo (it2->it) == 0) + // Only one content => unhighlight it (only for efficiency). + it1->it->unhighlight (0, layer); + else { + DeepIterator *it = it1->it->cloneDeepIterator (); + for (; it->compareTo (it2->it) <= 0; it->next ()) + it->unhighlight (-1, layer); + delete it; + } +} + +} // namespace core +} // namespace dw diff --git a/dw/iterator.hh b/dw/iterator.hh new file mode 100644 index 0000000..abf31d0 --- /dev/null +++ b/dw/iterator.hh @@ -0,0 +1,271 @@ +#ifndef __ITERATOR_HH__ +#define __ITERATOR_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief Iterators are used to iterate through the contents of a widget. + * + * When using iterators, you should care about the results of + * dw::core::Widget::hasContents. + * + * \sa dw::core::Widget::iterator + */ +class Iterator: public lout::object::Comparable +{ +protected: + Iterator(Widget *widget, Content::Type mask, bool atEnd); + Iterator(Iterator &it); + ~Iterator(); + + Content content; + +private: + Widget *widget; + Content::Type mask; + +public: + bool equals (Object *other); + void intoStringBuffer(lout::misc::StringBuffer *sb); + + inline Widget *getWidget () { return widget; } + inline Content *getContent () { return &content; } + inline Content::Type getMask () { return mask; } + + virtual void unref (); + + /** + * \brief Move iterator forward and store content it. + * + * Returns true on success. + */ + virtual bool next () = 0; + + /** + * \brief Move iterator backward and store content it. + * + * Returns true on success. + */ + virtual bool prev () = 0; + + /** + * \brief Extend highlighted region to contain part of the current content. + * + * For text, start and end define the + * characters, otherwise, the shape is defined as [0, 1], i.e. for + * highlighting a whole dw::core::Content, pass 0 and >= 1. + * To unhighlight see also dw::core::Iterator::unhighlight. + */ + virtual void highlight (int start, int end, HighlightLayer layer) = 0; + + /** + * \brief Shrink highlighted region to no longer contain the + * current content. + * + * The direction parameter indicates whether the highlighted region should + * be reduced from the start (direction > 0) or from the end + * (direction < 0). If direction is 0 all content is unhighlighted. + */ + virtual void unhighlight (int direction, HighlightLayer layer) = 0; + + /** + * \brief Return the shape, which a part of the item, the iterator points + * on, allocates. + * + * The parameters start and end have the same meaning as in + * DwIterator::highlight(). + */ + virtual void getAllocation (int start, int end, Allocation *allocation) = 0; + + inline Iterator *cloneIterator () { return (Iterator*)clone(); } + + static void scrollTo (Iterator *it1, Iterator *it2, int start, int end, + HPosition hpos, VPosition vpos); + + virtual void print (); +}; + + +/** + * \brief This implementation of dw::core::Iterator can be used by widgets + * with no contents. + */ +class EmptyIterator: public Iterator +{ +private: + EmptyIterator (EmptyIterator &it); + +public: + EmptyIterator (Widget *widget, Content::Type mask, bool atEnd); + + lout::object::Object *clone(); + int compareTo(lout::object::Comparable *other); + bool next (); + bool prev (); + void highlight (int start, int end, HighlightLayer layer); + void unhighlight (int direction, HighlightLayer layer); + void getAllocation (int start, int end, Allocation *allocation); +}; + + +/** + * \brief This implementation of dw::core::Iterator can be used by widgets + * having one text word as contents + */ +class TextIterator: public Iterator +{ +private: + /** May be NULL, in this case, the next is skipped. */ + const char *text; + + TextIterator (TextIterator &it); + +public: + TextIterator (Widget *widget, Content::Type mask, bool atEnd, + const char *text); + + int compareTo(lout::object::Comparable *other); + + bool next (); + bool prev (); + void getAllocation (int start, int end, Allocation *allocation); +}; + + +/** + * \brief A stack of iterators, to iterate recursively through a widget tree. + * + * This class is similar to dw::core::Iterator, but not + * created by a widget, but explicitly from another iterator. Deep + * iterators do not have the limitation, that iteration is only done within + * a widget, instead, child widgets are iterated through recursively. + */ +class DeepIterator: public lout::object::Comparable +{ +private: + class Stack: public lout::container::typed::Vector<Iterator> + { + public: + inline Stack (): lout::container::typed::Vector<Iterator> (4, false) { } + ~Stack (); + inline Iterator *getTop () { return get (size () - 1); } + inline void push (Iterator *it) { put(it, -1); } + inline void pop() { getTop()->unref (); remove (size () - 1); } + }; + + Stack stack; + + static Iterator *searchDownward (Iterator *it, Content::Type mask, + bool fromEnd); + static Iterator *searchSideward (Iterator *it, Content::Type mask, + bool fromEnd); + + Content::Type mask; + Content content; + bool hasContents; + + inline DeepIterator () { } + + static Widget *getRespectiveParent (Widget *widget, Content::Type mask); + inline Widget *getRespectiveParent (Widget *widget) { + return getRespectiveParent (widget, mask); + } + + static int getRespectiveLevel (Widget *widget, Content::Type mask); + inline int getRespectiveLevel (Widget *widget) { + return getRespectiveLevel (widget, mask); + } + +public: + DeepIterator(Iterator *it); + ~DeepIterator(); + + lout::object::Object *clone (); + + DeepIterator *createVariant(Iterator *it); + inline Iterator *getTopIterator () { return stack.getTop(); } + inline Content *getContent () { return &content; } + + bool isEmpty (); + + bool next (); + bool prev (); + inline DeepIterator *cloneDeepIterator() { return (DeepIterator*)clone(); } + int compareTo(lout::object::Comparable *other); + + /** + * \brief Highlight a part of the current content. + * + * Unhighlight the current content by passing -1 as start (see also + * (dw::core::Iterator::unhighlight). For text, start and end define the + * characters, otherwise, the shape is defined as [0, 1], i.e. for + * highlighting a whole dw::core::Content, pass 0 and >= 1. + */ + inline void highlight (int start, int end, HighlightLayer layer) + { stack.getTop()->highlight (start, end, layer); } + + /** + * \brief Return the shape, which a part of the item, the iterator points + * on, allocates. + * + * The parameters start and end have the same meaning as in + * DwIterator::highlight(). + */ + inline void getAllocation (int start, int end, Allocation *allocation) + { stack.getTop()->getAllocation (start, end, allocation); } + + inline void unhighlight (int direction, HighlightLayer layer) + { stack.getTop()->unhighlight (direction, layer); } + + inline static void scrollTo (DeepIterator *it1, DeepIterator *it2, + int start, int end, + HPosition hpos, VPosition vpos) + { Iterator::scrollTo(it1->stack.getTop(), it2->stack.getTop(), + start, end, hpos, vpos); } +}; + +class CharIterator: public lout::object::Comparable +{ +public: + // START and END must not clash with any char value + // neither for signed nor unsigned char. + enum { START = 257, END = 258 }; + +private: + DeepIterator *it; + int pos, ch; + + CharIterator (); + +public: + CharIterator (Widget *widget, bool followReferences); + ~CharIterator (); + + lout::object::Object *clone(); + int compareTo(lout::object::Comparable *other); + + bool next (); + bool prev (); + inline int getChar() { return ch; } + inline CharIterator *cloneCharIterator() { return (CharIterator*)clone(); } + + static void highlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer); + static void unhighlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer); + + inline static void scrollTo (CharIterator *it1, CharIterator *it2, + HPosition hpos, VPosition vpos) + { DeepIterator::scrollTo(it1->it, it2->it, it1->pos, it2->pos, + hpos, vpos); } +}; + +} // namespace core +} // namespace dw + +#endif // __ITERATOR_HH__ diff --git a/dw/layout.cc b/dw/layout.cc new file mode 100644 index 0000000..96de6c2 --- /dev/null +++ b/dw/layout.cc @@ -0,0 +1,1445 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" + +#include "../lout/msg.h" +#include "../lout/debug.hh" +#include "../lout/misc.hh" + +using namespace lout; +using namespace lout::container; +using namespace lout::object; + +namespace dw { +namespace core { + +bool Layout::LayoutImgRenderer::readyToDraw () +{ + return true; +} + +void Layout::LayoutImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + // TODO Actually not padding area, but visible area? + getRefArea (x, y, width, height); +} + +void Layout::LayoutImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) +{ + *xRef = 0; + *yRef = 0; + *widthRef = misc::max (layout->viewportWidth + - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0), + layout->canvasWidth); + *heightRef = misc::max (layout->viewportHeight + - layout->hScrollbarThickness, + layout->canvasAscent + layout->canvasDescent); +} + +style::StyleImage *Layout::LayoutImgRenderer::getBackgroundImage () +{ + return layout->bgImage; +} + +style::BackgroundRepeat Layout::LayoutImgRenderer::getBackgroundRepeat () +{ + return layout->bgRepeat; +} + +style::BackgroundAttachment + Layout::LayoutImgRenderer::getBackgroundAttachment () +{ + return layout->bgAttachment; +} + +style::Length Layout::LayoutImgRenderer::getBackgroundPositionX () +{ + return layout->bgPositionX; +} + +style::Length Layout::LayoutImgRenderer::getBackgroundPositionY () +{ + return layout->bgPositionY; +} + +void Layout::LayoutImgRenderer::draw (int x, int y, int width, int height) +{ + layout->queueDraw (x, y, width, height); +} + +// ---------------------------------------------------------------------- + +void Layout::Receiver::resizeQueued (bool extremesChanged) +{ +} + +void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent) +{ +} + +// ---------------------------------------------------------------------- + +bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver, + int signalNo, int argc, + lout::object::Object **argv) +{ + Receiver *layoutReceiver = (Receiver*)receiver; + + switch (signalNo) { + case CANVAS_SIZE_CHANGED: + layoutReceiver->canvasSizeChanged (((Integer*)argv[0])->getValue (), + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue ()); + break; + + case RESIZE_QUEUED: + layoutReceiver->resizeQueued (((Boolean*)argv[0])->getValue ()); + break; + + default: + misc::assertNotReached (); + } + + return false; +} + +void Layout::Emitter::emitResizeQueued (bool extremesChanged) +{ + Boolean ec (extremesChanged); + Object *argv[1] = { &ec }; + emitVoid (RESIZE_QUEUED, 1, argv); +} + +void Layout::Emitter::emitCanvasSizeChanged (int width, + int ascent, int descent) +{ + Integer w (width), a (ascent), d (descent); + Object *argv[3] = { &w, &a, &d }; + emitVoid (CANVAS_SIZE_CHANGED, 3, argv); +} + +// ---------------------------------------------------------------------- + +bool Layout::LinkReceiver::enter (Widget *widget, int link, int img, + int x, int y) +{ + return false; +} + +bool Layout::LinkReceiver::press (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +bool Layout::LinkReceiver::release (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +bool Layout::LinkReceiver::click (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +// ---------------------------------------------------------------------- + +bool Layout::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver, + int signalNo, int argc, + lout::object::Object **argv) +{ + LinkReceiver *linkReceiver = (LinkReceiver*)receiver; + + switch (signalNo) { + case ENTER: + return linkReceiver->enter ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue ()); + + case PRESS: + return linkReceiver->press ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + case RELEASE: + return linkReceiver->release ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + case CLICK: + return linkReceiver->click ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + default: + misc::assertNotReached (); + } + return false; +} + +bool Layout::LinkEmitter::emitEnter (Widget *widget, int link, int img, + int x, int y) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy }; + return emitBool (ENTER, 5, argv); +} + +bool Layout::LinkEmitter::emitPress (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (PRESS, 6, argv); +} + +bool Layout::LinkEmitter::emitRelease (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (RELEASE, 6, argv); +} + +bool Layout::LinkEmitter::emitClick (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (CLICK, 6, argv); +} + +// --------------------------------------------------------------------- + +Layout::Anchor::~Anchor () +{ + free(name); +} + +// --------------------------------------------------------------------- + +Layout::ScrollTarget::~ScrollTarget () +{ +} + +Layout::ScrollTargetBase::ScrollTargetBase (HPosition hPos, VPosition vPos) +{ + this->hPos = hPos; + this->vPos = vPos; +} + +HPosition Layout::ScrollTargetBase::getHPos () +{ + return hPos; +} + +VPosition Layout::ScrollTargetBase::getVPos () +{ + return vPos; +} + +Layout::ScrollTargetFixed::ScrollTargetFixed (HPosition hPos, VPosition vPos, + int x, int y, int width, int height) : + ScrollTargetBase (hPos, vPos) +{ + this->x = x; + this->y = y; + this->width = width; + this->height = height; +} + +int Layout::ScrollTargetFixed::getX () +{ + return x; +} + +int Layout::ScrollTargetFixed::getY () +{ + return y; +} + +int Layout::ScrollTargetFixed::getWidth () +{ + return width; +} + +int Layout::ScrollTargetFixed::getHeight () +{ + return height; +} + +Layout::ScrollTargetWidget::ScrollTargetWidget (HPosition hPos, VPosition vPos, + Widget *widget) : + ScrollTargetBase (hPos, vPos) +{ + this->widget = widget; +} + +int Layout::ScrollTargetWidget::getX () +{ + return widget->allocation.x; +} + +int Layout::ScrollTargetWidget::getY () +{ + return widget->allocation.y; +} + +int Layout::ScrollTargetWidget::getWidth () +{ + return widget->allocation.width; +} + +int Layout::ScrollTargetWidget::getHeight () +{ + return widget->allocation.ascent + widget->allocation.descent; +} + +// ---------------------------------------------------------------------- + +Layout::Layout (Platform *platform) +{ + this->platform = platform; + view = NULL; + topLevel = NULL; + widgetAtPoint = NULL; + + queueQueueResizeList = new typed::Stack<QueueResizeItem> (true); + queueResizeList = new typed::Vector<Widget> (4, false); + + DBG_OBJ_CREATE ("dw::core::Layout"); + + bgColor = NULL; + bgImage = NULL; + cursor = style::CURSOR_DEFAULT; + + canvasWidth = canvasAscent = canvasDescent = 0; + + usesViewport = false; + drawAfterScrollReq = false; + scrollX = scrollY = 0; + viewportWidth = viewportHeight = 0; + hScrollbarThickness = vScrollbarThickness = 0; + + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); + + requestedAnchor = NULL; + scrollTarget = NULL; + scrollIdleId = -1; + scrollIdleNotInterrupted = false; + + anchorsTable = + new container::typed::HashTable <object::String, Anchor> (true, true); + + resizeIdleId = -1; + + textZone = new misc::ZoneAllocator (16 * 1024); + + DBG_OBJ_ASSOC_CHILD (&findtextState); + DBG_OBJ_ASSOC_CHILD (&selectionState); + + platform->setLayout (this); + + selectionState.setLayout(this); + + queueResizeCounter = sizeAllocateCounter = sizeRequestCounter = + getExtremesCounter = 0; + + layoutImgRenderer = NULL; + + resizeIdleCounter = queueResizeCounter = sizeAllocateCounter + = sizeRequestCounter = getExtremesCounter = 0; +} + +Layout::~Layout () +{ + widgetAtPoint = NULL; + + if (layoutImgRenderer) { + if (bgImage) + bgImage->removeExternalImgRenderer (layoutImgRenderer); + delete layoutImgRenderer; + } + + if (scrollIdleId != -1) + platform->removeIdle (scrollIdleId); + if (resizeIdleId != -1) + platform->removeIdle (resizeIdleId); + if (bgColor) + bgColor->unref (); + if (bgImage) + bgImage->unref (); + if (topLevel) { + detachWidget (topLevel); + Widget *w = topLevel; + topLevel = NULL; + delete w; + } + + delete queueQueueResizeList; + delete queueResizeList; + delete platform; + delete view; + delete anchorsTable; + delete textZone; + + if (requestedAnchor) + free (requestedAnchor); + if (scrollTarget) + delete scrollTarget; + + DBG_OBJ_DELETE (); +} + +void Layout::detachWidget (Widget *widget) +{ + // Called form ~Layout. Sometimes, the widgets (not only the toplevel widget) + // do some stuff after the layout has been deleted, so *all* widgets have to + // be detached, and check "layout != NULL" at relevant points. + + // Could be replaced by a virtual method in Widget, like getWidgetAtPoint, + // if performace were really a problem. + + widget->layout = NULL; + Iterator *it = + widget->iterator ((Content::Type) + (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + detachWidget (it->getContent()->widget); + + it->unref (); +} + +void Layout::addWidget (Widget *widget) +{ + if (topLevel) { + MSG_WARN("widget already set\n"); + return; + } + + topLevel = widget; + widget->layout = this; + widget->container = NULL; + DBG_OBJ_SET_PTR_O (widget, "container", widget->container); + + queueResizeList->clear (); + widget->notifySetAsTopLevel (); + + findtextState.setWidget (widget); + + canvasHeightGreater = false; + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + + // Do not directly call Layout::queueResize(), but + // Widget::queueResize(), so that all flags are set properly, + // queueResizeList is filled, etc. + topLevel->queueResize (-1, false); +} + +void Layout::removeWidget () +{ + /** + * \bug Some more attributes must be reset here. + */ + topLevel = NULL; + queueResizeList->clear (); + widgetAtPoint = NULL; + canvasWidth = canvasAscent = canvasDescent = 0; + scrollX = scrollY = 0; + + view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); + if (view->usesViewport ()) + view->setViewportSize (viewportWidth, viewportHeight, 0, 0); + view->queueDrawTotal (); + + setAnchor (NULL); + updateAnchor (); + + emitter.emitCanvasSizeChanged (canvasWidth, canvasAscent, canvasDescent); + + findtextState.setWidget (NULL); + selectionState.reset (); + + updateCursor (); +} + +void Layout::setWidget (Widget *widget) +{ + DBG_OBJ_ASSOC_CHILD (widget); + + widgetAtPoint = NULL; + if (topLevel) { + Widget *w = topLevel; + topLevel = NULL; + delete w; + } + textZone->zoneFree (); + addWidget (widget); + + updateCursor (); +} + +/** + * \brief Attach a view to the layout. + * + * It will become a child of the layout, + * and so it will be destroyed, when the layout will be destroyed. + */ +void Layout::attachView (View *view) +{ + if (this->view) + MSG_ERR("attachView: Multiple views for layout!\n"); + + DBG_OBJ_ASSOC_CHILD (view); + + this->view = view; + platform->attachView (view); + + /* + * The layout of the view is set later, first, we "project" the current + * state of the layout into the new view. A view must handle this without + * a layout. See also at the end of this function. + */ + if (bgColor) + view->setBgColor (bgColor); + view->setCursor (cursor); + view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); + + if (view->usesViewport ()) { + if (usesViewport) { + view->scrollTo (scrollX, scrollY); + view->setViewportSize (viewportWidth, viewportHeight, + hScrollbarThickness, vScrollbarThickness); + hScrollbarThickness = misc::max (hScrollbarThickness, + view->getHScrollbarThickness ()); + vScrollbarThickness = misc::max (vScrollbarThickness, + view->getVScrollbarThickness ()); + } + else { + usesViewport = true; + scrollX = scrollY = 0; + viewportWidth = viewportHeight = 100; // random values + hScrollbarThickness = view->getHScrollbarThickness (); + vScrollbarThickness = view->getVScrollbarThickness (); + } + + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); + } + + /* + * This is the last call within this function, so that it is safe for + * the implementation of dw::core::View::setLayout, to call methods + * of dw::core::Layout. + */ + view->setLayout (this); +} + +void Layout::detachView (View *view) +{ + if (this->view != view) + MSG_ERR("detachView: this->view: %p view %p\n", this->view, view); + + view->setLayout (NULL); + platform->detachView (view); + this->view = NULL; + /** + * \todo Actually, viewportMarkerWidthDiff and + * viewportMarkerHeightDiff have to be recalculated here, since the + * effective (i.e. maximal) values may change, after the view has been + * detached. Same applies to the usage of viewports. + */ +} + +void Layout::scroll(ScrollCommand cmd) +{ + if (view->usesViewport ()) + view->scroll(cmd); +} + +/** + * \brief Scrolls all viewports, so that the region [x, y, width, height] + * is seen, according to hpos and vpos. + */ +void Layout::scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height) +{ + scrollTo0 (new ScrollTargetFixed (hpos, vpos, x, y, width, height), true); +} + +void Layout::scrollToWidget (HPosition hpos, VPosition vpos, Widget *widget) +{ + scrollTo0 (new ScrollTargetWidget (hpos, vpos, widget), true); +} + +void Layout::scrollTo0 (ScrollTarget *scrollTarget, bool scrollingInterrupted) +{ + if (usesViewport) { + _MSG("scrollTo (%d, %d, %s)\n", + x, y, scrollingInterrupted ? "true" : "false"); + + if (this->scrollTarget) + delete this->scrollTarget; + + this->scrollTarget = scrollTarget; + + if (scrollIdleId == -1) { + scrollIdleId = platform->addIdle (&Layout::scrollIdle); + scrollIdleNotInterrupted = true; + } + + scrollIdleNotInterrupted = + scrollIdleNotInterrupted || !scrollingInterrupted; + } +} + +void Layout::scrollIdle () +{ + DBG_OBJ_ENTER0 ("scroll", 0, "scrollIdle"); + + bool xChanged = true; + switch (scrollTarget->getHPos ()) { + case HPOS_LEFT: + scrollX = scrollTarget->getX (); + break; + case HPOS_CENTER: + scrollX = + scrollTarget->getX () - (viewportWidth - currVScrollbarThickness() + - scrollTarget->getWidth ()) / 2; + break; + case HPOS_RIGHT: + scrollX = + scrollTarget->getX () - (viewportWidth - currVScrollbarThickness() + - scrollTarget->getWidth ()); + break; + case HPOS_INTO_VIEW: + xChanged = calcScrollInto (scrollTarget->getX (), + scrollTarget->getWidth (), &scrollX, + viewportWidth - currVScrollbarThickness()); + break; + case HPOS_NO_CHANGE: + xChanged = false; + break; + } + + bool yChanged = true; + switch (scrollTarget->getVPos ()) { + case VPOS_TOP: + scrollY = scrollTarget->getY (); + break; + case VPOS_CENTER: + scrollY = + scrollTarget->getY () - (viewportHeight - currHScrollbarThickness() + - scrollTarget->getHeight ()) / 2; + break; + case VPOS_BOTTOM: + scrollY = + scrollTarget->getY () - (viewportHeight - currHScrollbarThickness() + - scrollTarget->getHeight ()); + break; + case VPOS_INTO_VIEW: + yChanged = calcScrollInto (scrollTarget->getY (), + scrollTarget->getHeight (), &scrollY, + viewportHeight - currHScrollbarThickness()); + break; + case VPOS_NO_CHANGE: + yChanged = false; + break; + } + + DBG_OBJ_MSGF ("scroll", 1, "xChanged = %s, yChanged = %s", + xChanged ? "true" : "false", yChanged ? "true" : "false"); + + if (xChanged || yChanged) { + adjustScrollPos (); + view->scrollTo (scrollX, scrollY); + } + + // The following code was once inside the block above ("if + // (xChanged || yChanged)"). I'm not sure but it looks that this + // should be outside, or at least setting drawAfterScrollReq in + // Layout::draw should consider whether the scroll position will + // change (but this would be rather difficult). --SG + + if (drawAfterScrollReq) { + drawAfterScrollReq = false; + view->queueDrawTotal (); + } + + delete scrollTarget; + scrollTarget = NULL; + + scrollIdleId = -1; + + DBG_OBJ_LEAVE (); +} + +void Layout::adjustScrollPos () +{ + scrollX = misc::min (scrollX, + canvasWidth - (viewportWidth - vScrollbarThickness)); + scrollX = misc::max (scrollX, 0); + + scrollY = misc::min (scrollY, + canvasAscent + canvasDescent - (viewportHeight - hScrollbarThickness)); + scrollY = misc::max (scrollY, 0); + + _MSG("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY); +} + +bool Layout::calcScrollInto (int requestedValue, int requestedSize, + int *value, int viewportSize) +{ + if (requestedSize > viewportSize) { + // The viewport size is smaller than the size of the region which will + // be shown. If the region is already visible, do not change the + // position. Otherwise, show the left/upper border, this is most likely + // what is needed. + if (*value >= requestedValue && + *value + viewportSize < requestedValue + requestedSize) + return false; + else + requestedSize = viewportSize; + } + + if (requestedValue < *value) { + *value = requestedValue; + return true; + } else if (requestedValue + requestedSize > *value + viewportSize) { + *value = requestedValue - viewportSize + requestedSize; + return true; + } else + return false; +} + +void Layout::draw (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + Rectangle widgetArea, intersection, widgetDrawArea; + + // First of all, draw background image. (Unlike background *color*, + // this is not a feature of the views.) + if (bgImage != NULL && bgImage->getImgbufSrc() != NULL) + style::drawBackgroundImage (view, bgImage, bgRepeat, bgAttachment, + bgPositionX, bgPositionY, + area->x, area->y, area->width, + area->height, 0, 0, + // Reference area: maximum of canvas size and + // viewport size. + misc::max (viewportWidth + - (canvasHeightGreater ? + vScrollbarThickness : 0), + canvasWidth), + misc::max (viewportHeight + - hScrollbarThickness, + canvasAscent + canvasDescent)); + + if (scrollIdleId != -1) { + /* scroll is pending, defer draw until after scrollIdle() */ + drawAfterScrollReq = true; + DBG_OBJ_MSGF ("draw", 1, "scrollIdleId = %d => delayed", scrollIdleId); + } else if (topLevel) { + /* Draw the top level widget. */ + widgetArea.x = topLevel->allocation.x; + widgetArea.y = topLevel->allocation.y; + widgetArea.width = topLevel->allocation.width; + widgetArea.height = topLevel->getHeight (); + + if (area->intersectsWith (&widgetArea, &intersection)) { + DBG_OBJ_MSG ("draw", 1, "drawing toplevel widget"); + + view->startDrawing (&intersection); + + /* Intersection in widget coordinates. */ + widgetDrawArea.x = intersection.x - topLevel->allocation.x; + widgetDrawArea.y = intersection.y - topLevel->allocation.y; + widgetDrawArea.width = intersection.width; + widgetDrawArea.height = intersection.height; + + topLevel->draw (view, &widgetDrawArea); + + view->finishDrawing (&intersection); + } else + DBG_OBJ_MSG ("draw", 1, "no intersection"); + } else + DBG_OBJ_MSG ("draw", 1, "no toplevel widget"); + + DBG_OBJ_LEAVE (); +} + +int Layout::currHScrollbarThickness() +{ + return (canvasWidth > viewportWidth) ? hScrollbarThickness : 0; +} + +int Layout::currVScrollbarThickness() +{ + return (canvasAscent + canvasDescent > viewportHeight) ? + vScrollbarThickness : 0; +} + +/** + * Sets the anchor to scroll to. + */ +void Layout::setAnchor (const char *anchor) +{ + _MSG("setAnchor (%s)\n", anchor); + + if (requestedAnchor) + free (requestedAnchor); + requestedAnchor = anchor ? strdup (anchor) : NULL; + updateAnchor (); +} + +/** + * Used, when the widget is not allocated yet. + */ +char *Layout::addAnchor (Widget *widget, const char* name) +{ + return addAnchor (widget, name, -1); +} + +char *Layout::addAnchor (Widget *widget, const char* name, int y) +{ + String key (name); + if (anchorsTable->contains (&key)) + return NULL; + else { + Anchor *anchor = new Anchor (); + anchor->name = strdup (name); + anchor->widget = widget; + anchor->y = y; + + anchorsTable->put (new String (name), anchor); + updateAnchor (); + + return anchor->name; + } +} + +void Layout::changeAnchor (Widget *widget, char* name, int y) +{ + String key (name); + Anchor *anchor = anchorsTable->get (&key); + assert (anchor); + assert (anchor->widget == widget); + anchor->y = y; + updateAnchor (); +} + +void Layout::removeAnchor (Widget *widget, char* name) +{ + String key (name); + anchorsTable->remove (&key); +} + +void Layout::updateAnchor () +{ + Anchor *anchor; + if (requestedAnchor) { + String key (requestedAnchor); + anchor = anchorsTable->get (&key); + } else + anchor = NULL; + + if (anchor == NULL) { + /** \todo Copy comment from old docs. */ + if (scrollIdleId != -1 && !scrollIdleNotInterrupted) { + platform->removeIdle (scrollIdleId); + scrollIdleId = -1; + } + } else + if (anchor->y != -1) + scrollTo0 (new ScrollTargetFixed (HPOS_NO_CHANGE, VPOS_TOP, + 0, anchor->y, 0, 0), false); +} + +void Layout::setCursor (style::Cursor cursor) +{ + if (cursor != this->cursor) { + this->cursor = cursor; + view->setCursor (cursor); + } +} + +void Layout::updateCursor () +{ + if (widgetAtPoint && widgetAtPoint->style) + setCursor (widgetAtPoint->style->cursor); + else + setCursor (style::CURSOR_DEFAULT); +} + +void Layout::setBgColor (style::Color *color) +{ + color->ref (); + + if (bgColor) + bgColor->unref (); + + bgColor = color; + + if (view) + view->setBgColor (bgColor); +} + +void Layout::setBgImage (style::StyleImage *bgImage, + style::BackgroundRepeat bgRepeat, + style::BackgroundAttachment bgAttachment, + style::Length bgPositionX, style::Length bgPositionY) +{ + if (layoutImgRenderer && this->bgImage) + this->bgImage->removeExternalImgRenderer (layoutImgRenderer); + + if (bgImage) + bgImage->ref (); + + if (this->bgImage) + this->bgImage->unref (); + + this->bgImage = bgImage; + this->bgRepeat = bgRepeat; + this->bgAttachment = bgAttachment; + this->bgPositionX = bgPositionX; + this->bgPositionY = bgPositionY; + + if (bgImage) { + // Create instance of LayoutImgRenderer when needed. Until this + // layout is deleted, "layoutImgRenderer" will be kept, since it + // is not specific to the style, but only to this layout. + if (layoutImgRenderer == NULL) + layoutImgRenderer = new LayoutImgRenderer (this); + bgImage->putExternalImgRenderer (layoutImgRenderer); + } +} + + +void Layout::resizeIdle () +{ + DBG_OBJ_ENTER0 ("resize", 0, "resizeIdle"); + + enterResizeIdle (); + + //static int calls = 0; + //printf ("Layout::resizeIdle calls = %d\n", ++calls); + + assert (resizeIdleId != -1); + + for (typed::Iterator <Widget> it = queueResizeList->iterator(); + it.hasNext (); ) { + Widget *widget = it.getNext (); + + //printf (" the %stop-level %s %p was queued (extremes changed: %s)\n", + // widget->parent ? "non-" : "", widget->getClassName(), widget, + // widget->extremesQueued () ? "yes" : "no"); + + if (widget->resizeQueued ()) { + widget->setFlags (Widget::NEEDS_RESIZE); + widget->unsetFlags (Widget::RESIZE_QUEUED); + } + + if (widget->allocateQueued ()) { + widget->setFlags (Widget::NEEDS_ALLOCATE); + widget->unsetFlags (Widget::ALLOCATE_QUEUED); + } + + if (widget->extremesQueued ()) { + widget->setFlags (Widget::EXTREMES_CHANGED); + widget->unsetFlags (Widget::EXTREMES_QUEUED); + } + } + queueResizeList->clear (); + + // Reset already here, since in this function, queueResize() may be + // called again. + resizeIdleId = -1; + + // If this method is triggered by a viewport change, we can save + // time when the toplevel widget is not affected (as for a toplevel + // image resource). + if (topLevel && (topLevel->needsResize () || topLevel->needsAllocate ())) { + Requisition requisition; + Allocation allocation; + + topLevel->sizeRequest (&requisition); + DBG_OBJ_MSGF ("resize", 1, "toplevel size: %d * (%d + %d)", + requisition.width, requisition.ascent, requisition.descent); + + // This method is triggered by Widget::queueResize, which will, + // in any case, set NEEDS_ALLOCATE (indirectly, as ALLOCATE_QUEUED). + // This assertion helps to find inconsistences. (Cases where + // this method is triggered by a viewport change, but the + // toplevel widget is not affected, are filtered out some lines + // above: "if (topLevel && topLevel->needsResize ())".) + assert (topLevel->needsAllocate ()); + + allocation.x = allocation.y = 0; + allocation.width = requisition.width; + allocation.ascent = requisition.ascent; + allocation.descent = requisition.descent; + topLevel->sizeAllocate (&allocation); + + canvasWidth = requisition.width; + canvasAscent = requisition.ascent; + canvasDescent = requisition.descent; + + emitter.emitCanvasSizeChanged (canvasWidth, canvasAscent, canvasDescent); + + // Tell the view about the new world size. + view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); + // view->queueDrawTotal (false); + + if (usesViewport) { + int currHThickness = currHScrollbarThickness(); + int currVThickness = currVScrollbarThickness(); + + if (!canvasHeightGreater && + canvasAscent + canvasDescent > viewportHeight - currHThickness) { + canvasHeightGreater = true; + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + containerSizeChanged (); + } + + // Set viewport sizes. + view->setViewportSize (viewportWidth, viewportHeight, + currHThickness, currVThickness); + } + + // views are redrawn via Widget::resizeDrawImpl () + } + + updateAnchor (); + + DBG_OBJ_MSGF ("resize", 1, + "after resizeIdle: resizeIdleId = %d", resizeIdleId); + DBG_OBJ_LEAVE (); + + leaveResizeIdle (); +} + +void Layout::queueDraw (int x, int y, int width, int height) +{ + DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d", + x, y, width, height); + + Rectangle area; + area.x = x; + area.y = y; + area.width = width; + area.height = height; + + if (!area.isEmpty ()) + view->queueDraw (&area); + + DBG_OBJ_LEAVE (); +} + +void Layout::queueDrawExcept (int x, int y, int width, int height, + int ex, int ey, int ewidth, int eheight) { + + if (x == ex && y == ey && width == ewidth && height == eheight) + return; + + // queueDraw() the four rectangles within rectangle (x, y, width, height) + // around rectangle (ex, ey, ewidth, eheight). + // Some or all of these may be empty. + + // upper left corner of the intersection rectangle + int ix1 = misc::max (x, ex); + int iy1 = misc::max (y, ey); + // lower right corner of the intersection rectangle + int ix2 = misc::min (x + width, ex + ewidth); + int iy2 = misc::min (y + height, ey + eheight); + + queueDraw (x, y, width, iy1 - y); + queueDraw (x, iy2, width, y + height - iy2); + queueDraw (x, iy1, ix1 - x, iy2 - iy1); + queueDraw (ix2, iy1, x + width - ix2, iy2 - iy1); +} + +void Layout::queueResize (bool extremesChanged) +{ + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%s", + extremesChanged ? "true" : "false"); + + if (resizeIdleId == -1) { + view->cancelQueueDraw (); + + resizeIdleId = platform->addIdle (&Layout::resizeIdle); + DBG_OBJ_MSGF ("resize", 1, "setting resizeIdleId = %d", resizeIdleId); + } + + emitter.emitResizeQueued (extremesChanged); + + DBG_OBJ_LEAVE (); +} + + +// Views + +bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed, + int x, int y, ButtonState state, int button) + +{ + EventButton event; + + moveToWidgetAtPoint (x, y, state); + + event.xCanvas = x; + event.yCanvas = y; + event.state = state; + event.button = button; + event.numPressed = numPressed; + + return processMouseEvent (&event, type); +} + +/** + * \brief This function is called by a view, to delegate a motion notify + * event. + * + * Arguments are similar to dw::core::Layout::buttonPress. + */ +bool Layout::motionNotify (View *view, int x, int y, ButtonState state) +{ + EventButton event; + + moveToWidgetAtPoint (x, y, state); + + event.xCanvas = x; + event.yCanvas = y; + event.state = state; + + return processMouseEvent (&event, MOTION_NOTIFY); +} + +/** + * \brief This function is called by a view, to delegate a enter notify event. + * + * Arguments are similar to dw::core::Layout::buttonPress. + */ +void Layout::enterNotify (View *view, int x, int y, ButtonState state) +{ + Widget *lastWidget; + EventCrossing event; + + lastWidget = widgetAtPoint; + moveToWidgetAtPoint (x, y, state); + + if (widgetAtPoint) { + event.state = state; + event.lastWidget = lastWidget; + event.currentWidget = widgetAtPoint; + widgetAtPoint->enterNotify (&event); + } +} + +/** + * \brief This function is called by a view, to delegate a leave notify event. + * + * Arguments are similar to dw::core::Layout::buttonPress. + */ +void Layout::leaveNotify (View *view, ButtonState state) +{ +#if 0 + Widget *lastWidget; + EventCrossing event; + + lastWidget = widgetAtPoint; + moveOutOfView (state); + + if (lastWidget) { + event.state = state; + event.lastWidget = lastWidget; + event.currentWidget = widgetAtPoint; + lastWidget->leaveNotify (&event); + } +#else + moveOutOfView (state); +#endif +} + +/* + * Return the widget at position (x, y). Return NULL, if there is no widget. + */ +Widget *Layout::getWidgetAtPoint (int x, int y) +{ + _MSG ("------------------------------------------------------------\n"); + _MSG ("widget at (%d, %d)\n", x, y); + if (topLevel && topLevel->wasAllocated ()) + return topLevel->getWidgetAtPoint (x, y, 0); + else + return NULL; +} + + +/* + * Emit the necessary crossing events, when the mouse pointer has moved to + * the given widget (by mouse or scrolling). + */ +void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) +{ + Widget *ancestor, *w; + Widget **track; + int trackLen, i, i_a; + EventCrossing crossingEvent; + + _MSG("moveToWidget: wap=%p nwap=%p\n",widgetAtPoint,newWidgetAtPoint); + if (newWidgetAtPoint != widgetAtPoint) { + // The mouse pointer has been moved into another widget. + if (newWidgetAtPoint && widgetAtPoint) + ancestor = + newWidgetAtPoint->getNearestCommonAncestor (widgetAtPoint); + else if (newWidgetAtPoint) + ancestor = newWidgetAtPoint->getTopLevel (); + else + ancestor = widgetAtPoint->getTopLevel (); + + // Construct the track. + trackLen = 0; + if (widgetAtPoint) + // first part + for (w = widgetAtPoint; w != ancestor; w = w->getParent ()) + trackLen++; + trackLen++; // for the ancestor + if (newWidgetAtPoint) + // second part + for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ()) + trackLen++; + + track = new Widget* [trackLen]; + i = 0; + if (widgetAtPoint) + /* first part */ + for (w = widgetAtPoint; w != ancestor; w = w->getParent ()) + track[i++] = w; + i_a = i; + track[i++] = ancestor; + if (newWidgetAtPoint) { + /* second part */ + i = trackLen - 1; + for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ()) + track[i--] = w; + } +#if 0 + MSG("Track: %s[ ", widgetAtPoint ? "" : "nil "); + for (i = 0; i < trackLen; i++) + MSG("%s%p ", i == i_a ? ">" : "", track[i]); + MSG("] %s\n", newWidgetAtPoint ? "" : "nil"); +#endif + + /* Send events to the widgets on the track */ + for (i = 0; i < trackLen; i++) { + crossingEvent.state = state; + crossingEvent.currentWidget = widgetAtPoint; // ??? + crossingEvent.lastWidget = widgetAtPoint; // ??? + if (i < i_a) { + track[i]->leaveNotify (&crossingEvent); + } else if (i == i_a) { /* ancestor */ + /* Don't touch ancestor unless: + * - moving into/from NULL, + * - ancestor becomes the newWidgetAtPoint */ + if (i_a == trackLen-1 && !newWidgetAtPoint) + track[i]->leaveNotify (&crossingEvent); + else if ((i_a == 0 && !widgetAtPoint) || + (i_a == trackLen-1 && newWidgetAtPoint)) + track[i]->enterNotify (&crossingEvent); + } else { + track[i]->enterNotify (&crossingEvent); + } + } + + delete[] track; + + widgetAtPoint = newWidgetAtPoint; + updateCursor (); + } +} + +/** + * \brief Common processing of press, release and motion events. + * + * This function depends on that move_to_widget_at_point() + * has been called before. + */ +bool Layout::processMouseEvent (MousePositionEvent *event, + ButtonEventType type) +{ + Widget *widget; + + /* + * If the event is outside of the visible region of the canvas, treat it + * as occurring at the region's edge. Notably, this helps when selecting + * text. + */ + if (event->xCanvas < scrollX) + event->xCanvas = scrollX; + else { + int maxX = scrollX + viewportWidth - currVScrollbarThickness() - 1; + + if (event->xCanvas > maxX) + event->xCanvas = maxX; + } + if (event->yCanvas < scrollY) + event->yCanvas = scrollY; + else { + int maxY = misc::min(scrollY + viewportHeight -currHScrollbarThickness(), + canvasAscent + canvasDescent) - 1; + + if (event->yCanvas > maxY) + event->yCanvas = maxY; + } + + widget = getWidgetAtPoint(event->xCanvas, event->yCanvas); + + for (; widget; widget = widget->getParent ()) { + if (widget->isButtonSensitive ()) { + event->xWidget = event->xCanvas - widget->getAllocation()->x; + event->yWidget = event->yCanvas - widget->getAllocation()->y; + + switch (type) { + case BUTTON_PRESS: + return widget->buttonPress ((EventButton*)event); + + case BUTTON_RELEASE: + return widget->buttonRelease ((EventButton*)event); + + case MOTION_NOTIFY: + return widget->motionNotify ((EventMotion*)event); + + default: + misc::assertNotReached (); + } + } + } + if (type == BUTTON_PRESS) + return emitLinkPress (NULL, -1, -1, -1, -1, (EventButton*)event); + else if (type == BUTTON_RELEASE) + return emitLinkRelease(NULL, -1, -1, -1, -1, (EventButton*)event); + + return false; +} + +/* + * This function must be called by a view, when the user has manually changed + * the viewport position. It is *not* called, when the layout has requested the + * position change. + */ +void Layout::scrollPosChanged (View *view, int x, int y) +{ + if (x != scrollX || y != scrollY) { + scrollX = x; + scrollY = y; + + setAnchor (NULL); + updateAnchor (); + } +} + +/* + * This function must be called by a viewport view, when its viewport size has + * changed. It is *not* called, when the layout has requested the size change. + */ +void Layout::viewportSizeChanged (View *view, int width, int height) +{ + DBG_OBJ_ENTER ("resize", 0, "viewportSizeChanged", "%p, %d, %d", + view, width, height); + + /* If the width has become higher, we test again, whether the vertical + * scrollbar (so to speak) can be hidden again. */ + if (usesViewport && width > viewportWidth) { + canvasHeightGreater = false; + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + } + + /* if size changes, redraw this view. + * TODO: this is a resize call (redraw/resize code needs a review). */ + if (viewportWidth != width || viewportHeight != height) { + if (topLevel) + // similar to addWidget() + topLevel->queueResize (-1, false); + else + queueResize (false); + } + + viewportWidth = width; + viewportHeight = height; + + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + + containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +void Layout::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + if (topLevel) { + topLevel->containerSizeChanged (); + queueResize (true); + } + + DBG_OBJ_LEAVE (); +} + +} // namespace core +} // namespace dw diff --git a/dw/layout.hh b/dw/layout.hh new file mode 100644 index 0000000..dbcff99 --- /dev/null +++ b/dw/layout.hh @@ -0,0 +1,532 @@ +#ifndef __DW_LAYOUT_HH__ +#define __DW_LAYOUT_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief The central class for managing and drawing a widget tree. + * + * \sa\ref dw-overview, \ref dw-layout-widgets, \ref dw-layout-views + */ +class Layout: public lout::object::Object +{ + friend class Widget; + +private: + class LayoutImgRenderer: public style::StyleImage::ExternalImgRenderer + { + Layout *layout; + + public: + LayoutImgRenderer (Layout *layout) { this->layout = layout; } + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + style::StyleImage *getBackgroundImage (); + style::BackgroundRepeat getBackgroundRepeat (); + style::BackgroundAttachment getBackgroundAttachment (); + style::Length getBackgroundPositionX (); + style::Length getBackgroundPositionY (); + void draw (int x, int y, int width, int height); + }; + + LayoutImgRenderer *layoutImgRenderer; + +public: + /** + * \brief Receiver interface different signals. + * + * May be extended. + */ + class Receiver: public lout::signal::Receiver + { + public: + virtual void resizeQueued (bool extremesChanged); + virtual void canvasSizeChanged (int width, int ascent, int descent); + }; + + class LinkReceiver: public lout::signal::Receiver + { + public: + /** + * \brief Called, when a link is entered, left, or the position has + * changed. + * + * When a link is entered, this method is called with the respective + * arguments. When a link is left, this method is called with all + * three arguments (\em link, \em x, \em y) set to -1. + * + * When coordinates are supported, a change of the coordinates also + * causes emitting this signal. + */ + virtual bool enter (Widget *widget, int link, int img, int x, int y); + + /** + * \brief Called, when the user has pressed the mouse button on a + * link (but not yet released). + * + * The causing event is passed as \em event. + */ + virtual bool press (Widget *widget, int link, int img, int x, int y, + EventButton *event); + + /** + * \brief Called, when the user has released the mouse button on a + * link. + * + * The causing event is passed as \em event. + */ + virtual bool release (Widget *widget, int link, int img, int x, int y, + EventButton *event); + + /** + * \brief Called, when the user has clicked on a link. + * + * For mouse interaction, this is equivalent to "press" and "release" + * on the same link. In this case, \em event contains the "release" + * event. + * + * + * When activating links via keyboard is supported, only a "clicked" + * signal will be emitted, and \em event will be NULL. + */ + virtual bool click (Widget *widget, int link, int img, int x, int y, + EventButton *event); + }; + + class LinkEmitter: public lout::signal::Emitter + { + private: + enum { ENTER, PRESS, RELEASE, CLICK }; + + protected: + bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, + int argc, lout::object::Object **argv); + + public: + inline void connectLink (LinkReceiver *receiver) { connect (receiver); } + + bool emitEnter (Widget *widget, int link, int img, int x, int y); + bool emitPress (Widget *widget, int link, int img, int x, int y, + EventButton *event); + bool emitRelease (Widget *widget, int link, int img, int x, int y, + EventButton *event); + bool emitClick (Widget *widget, int link, int img, int x, int y, + EventButton *event); + }; + + LinkEmitter linkEmitter; + +private: + class Emitter: public lout::signal::Emitter + { + private: + enum { RESIZE_QUEUED, CANVAS_SIZE_CHANGED }; + + protected: + bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, + int argc, lout::object::Object **argv); + + public: + inline void connectLayout (Receiver *receiver) { connect (receiver); } + + void emitResizeQueued (bool extremesChanged); + void emitCanvasSizeChanged (int width, int ascent, int descent); + }; + + Emitter emitter; + + class Anchor: public lout::object::Object + { + public: + char *name; + Widget *widget; + int y; + + ~Anchor (); + }; + + class QueueResizeItem: public lout::object::Object + { + public: + Widget *widget; + int ref; + bool extremesChanged, fast; + + inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged, + bool fast) + { + this->widget = widget; + this->ref = ref; + this->extremesChanged = extremesChanged; + this->fast = fast; + } + }; + + /** + * \brief An abstract scrolling target. The values are first + * calculated when they are needed in scrollIdle(). + * + * (Note: perhaps a subclass should be uses for anchors.) + */ + class ScrollTarget + { + public: + virtual ~ScrollTarget (); + + virtual HPosition getHPos () = 0; + virtual VPosition getVPos () = 0; + virtual int getX () = 0; + virtual int getY () = 0; + virtual int getWidth () = 0; + virtual int getHeight () = 0; + }; + + class ScrollTargetBase: public ScrollTarget + { + HPosition hPos; + VPosition vPos; + + public: + ScrollTargetBase (HPosition hPos, VPosition vPos); + + HPosition getHPos (); + VPosition getVPos (); + }; + + /** + * \brief Scrolling target with concrete values. + */ + class ScrollTargetFixed: public ScrollTargetBase + { + int x, y, width, height; + + public: + ScrollTargetFixed (HPosition hPos, VPosition vPos, + int x, int y, int width, int height); + + int getX (); + int getY (); + int getWidth (); + int getHeight (); + }; + + /** + * \brief Scrolling target for a widget allocation. + * + * If the widget is allocated between scrollToWidget() and + * scrollIdle(), this is taken into account. + */ + class ScrollTargetWidget: public ScrollTargetBase + { + Widget *widget; + + public: + ScrollTargetWidget (HPosition hPos, VPosition vPos, Widget *widget); + + int getX (); + int getY (); + int getWidth (); + int getHeight (); + }; + + Platform *platform; + View *view; + Widget *topLevel, *widgetAtPoint; + lout::container::typed::Stack<QueueResizeItem> *queueQueueResizeList; + lout::container::typed::Vector<Widget> *queueResizeList; + + /* The state, which must be projected into the view. */ + style::Color *bgColor; + style::StyleImage *bgImage; + style::BackgroundRepeat bgRepeat; + style::BackgroundAttachment bgAttachment; + style::Length bgPositionX, bgPositionY; + + style::Cursor cursor; + int canvasWidth, canvasAscent, canvasDescent; + + bool usesViewport, drawAfterScrollReq; + int scrollX, scrollY, viewportWidth, viewportHeight; + bool canvasHeightGreater; + int hScrollbarThickness, vScrollbarThickness; + + ScrollTarget *scrollTarget; + + char *requestedAnchor; + int scrollIdleId, resizeIdleId; + bool scrollIdleNotInterrupted; + + /* Anchors of the widget tree */ + lout::container::typed::HashTable <lout::object::String, Anchor> + *anchorsTable; + + SelectionState selectionState; + FindtextState findtextState; + + enum ButtonEventType { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY }; + + void detachWidget (Widget *widget); + + Widget *getWidgetAtPoint (int x, int y); + void moveToWidget (Widget *newWidgetAtPoint, ButtonState state); + + /** + * \brief Emit the necessary crossing events, when the mouse pointer has + * moved to position (\em x, \em ); + */ + void moveToWidgetAtPoint (int x, int y, ButtonState state) + { moveToWidget (getWidgetAtPoint (x, y), state); } + + /** + * \brief Emit the necessary crossing events, when the mouse pointer + * has moved out of the view. + */ + void moveOutOfView (ButtonState state) { moveToWidget (NULL, state); } + + bool processMouseEvent (MousePositionEvent *event, ButtonEventType type); + bool buttonEvent (ButtonEventType type, View *view, + int numPressed, int x, int y, ButtonState state, + int button); + void resizeIdle (); + void setSizeHints (); + void draw (View *view, Rectangle *area); + + void scrollTo0(ScrollTarget *scrollTarget, bool scrollingInterrupted); + void scrollIdle (); + void adjustScrollPos (); + static bool calcScrollInto (int targetValue, int requestedSize, + int *value, int viewportSize); + int currHScrollbarThickness(); + int currVScrollbarThickness(); + + void updateAnchor (); + + /* Widget */ + + char *addAnchor (Widget *widget, const char* name); + char *addAnchor (Widget *widget, const char* name, int y); + void changeAnchor (Widget *widget, char* name, int y); + void removeAnchor (Widget *widget, char* name); + void setCursor (style::Cursor cursor); + void updateCursor (); + void queueDraw (int x, int y, int width, int height); + void queueDrawExcept (int x, int y, int width, int height, + int ex, int ey, int ewidth, int eheight); + void queueResize (bool extremesChanged); + void removeWidget (); + + /* For tests regarding the respective Layout and (mostly) Widget + methods. Accessed by respective methods (enter..., leave..., + ...Entered) defined here and in Widget. */ + + int resizeIdleCounter, queueResizeCounter, sizeAllocateCounter, + sizeRequestCounter, getExtremesCounter; + + void enterResizeIdle () { resizeIdleCounter++; } + void leaveResizeIdle () { resizeIdleCounter--; } + +public: + Layout (Platform *platform); + ~Layout (); + + inline void connectLink (LinkReceiver *receiver) + { linkEmitter.connectLink (receiver); } + + inline bool emitLinkEnter (Widget *w, int link, int img, int x, int y) + { return linkEmitter.emitEnter (w, link, img, x, y); } + + inline bool emitLinkPress (Widget *w, int link, int img, + int x, int y, EventButton *event) + { return linkEmitter.emitPress (w, link, img, x, y, event); } + + inline bool emitLinkRelease (Widget *w, int link, int img, + int x, int y, EventButton *event) + { return linkEmitter.emitRelease (w, link, img, x, y, event); } + + inline bool emitLinkClick (Widget *w, int link, int img, + int x, int y, EventButton *event) + { return linkEmitter.emitClick (w, link, img, x, y, event); } + + lout::misc::ZoneAllocator *textZone; + + void addWidget (Widget *widget); + void setWidget (Widget *widget); + + void attachView (View *view); + void detachView (View *view); + + inline bool getUsesViewport () { return usesViewport; } + inline int getWidthViewport () { return viewportWidth; } + inline int getHeightViewport () { return viewportHeight; } + inline int getScrollPosX () { return scrollX; } + inline int getScrollPosY () { return scrollY; } + + /* public */ + + void scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height); + void scrollToWidget (HPosition hpos, VPosition vpos, Widget *widget); + void scroll (ScrollCommand cmd); + void setAnchor (const char *anchor); + + /* View */ + + inline void expose (View *view, Rectangle *area) { + DBG_OBJ_ENTER ("draw", 0, "expose", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + draw (view, area); + DBG_OBJ_LEAVE (); + } + + /** + * \brief This function is called by a view, to delegate a button press + * event. + * + * \em numPressed is 1 for simple presses, 2 for double presses etc. (more + * that 2 is never needed), \em x and \em y the world coordinates, and + * \em button the number of the button pressed. + */ + inline bool buttonPress (View *view, int numPressed, int x, int y, + ButtonState state, int button) + { + return buttonEvent (BUTTON_PRESS, view, numPressed, x, y, state, button); + } + + void containerSizeChanged (); + + /** + * \brief This function is called by a view, to delegate a button press + * event. + * + * Arguments are similar to dw::core::Layout::buttonPress. + */ + inline bool buttonRelease (View *view, int numPressed, int x, int y, + ButtonState state, int button) + { + return buttonEvent (BUTTON_RELEASE, view, numPressed, x, y, state, + button); + } + + bool motionNotify (View *view, int x, int y, ButtonState state); + void enterNotify (View *view, int x, int y, ButtonState state); + void leaveNotify (View *view, ButtonState state); + + void scrollPosChanged (View *view, int x, int y); + void viewportSizeChanged (View *view, int width, int height); + + inline Platform *getPlatform () + { + return platform; + } + + /* delegated */ + + inline int textWidth (style::Font *font, const char *text, int len) + { + return platform->textWidth (font, text, len); + } + + inline char *textToUpper (const char *text, int len) + { + return platform->textToUpper (text, len); + } + + inline char *textToLower (const char *text, int len) + { + return platform->textToLower (text, len); + } + + inline int nextGlyph (const char *text, int idx) + { + return platform->nextGlyph (text, idx); + } + + inline int prevGlyph (const char *text, int idx) + { + return platform->prevGlyph (text, idx); + } + + inline float dpiX () + { + return platform->dpiX (); + } + + inline float dpiY () + { + return platform->dpiY (); + } + + inline style::Font *createFont (style::FontAttrs *attrs, bool tryEverything) + { + return platform->createFont (attrs, tryEverything); + } + + inline bool fontExists (const char *name) + { + return platform->fontExists (name); + } + + inline style::Color *createColor (int color) + { + return platform->createColor (color); + } + + inline style::Tooltip *createTooltip (const char *text) + { + return platform->createTooltip (text); + } + + inline void cancelTooltip () + { + return platform->cancelTooltip (); + } + + inline Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height, + double gamma) + { + return platform->createImgbuf (type, width, height, gamma); + } + + inline void copySelection(const char *text) + { + platform->copySelection(text); + } + + inline ui::ResourceFactory *getResourceFactory () + { + return platform->getResourceFactory (); + } + + inline void connect (Receiver *receiver) { + emitter.connectLayout (receiver); } + + /** \brief See dw::core::FindtextState::search. */ + inline FindtextState::Result search (const char *str, bool caseSens, + int backwards) + { return findtextState.search (str, caseSens, backwards); } + + /** \brief See dw::core::FindtextState::resetSearch. */ + inline void resetSearch () { findtextState.resetSearch (); } + + void setBgColor (style::Color *color); + void setBgImage (style::StyleImage *bgImage, + style::BackgroundRepeat bgRepeat, + style::BackgroundAttachment bgAttachment, + style::Length bgPositionX, style::Length bgPositionY); + + inline style::Color* getBgColor () { return bgColor; } + inline style::StyleImage* getBgImage () { return bgImage; } +}; + +} // namespace core +} // namespace dw + +#endif // __DW_LAYOUT_HH__ + diff --git a/dw/platform.hh b/dw/platform.hh new file mode 100644 index 0000000..227cda3 --- /dev/null +++ b/dw/platform.hh @@ -0,0 +1,171 @@ +#ifndef __DW_PLATFORM_HH__ +#define __DW_PLATFORM_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief An interface to encapsulate some platform dependencies. + * + * \sa\ref dw-overview + */ +class Platform: public lout::object::Object +{ +public: + /* + * ----------------------------------- + * General + * ----------------------------------- + */ + + /** + * \brief This methods notifies the platform, that it has been attached to + * a layout. + */ + virtual void setLayout (Layout *layout) = 0; + + /* + * ------------------------- + * Operations on views + * ------------------------- + */ + + /** + * \brief This methods notifies the platform, that a view has been attached + * to the related layout. + */ + virtual void attachView (View *view) = 0; + + /** + * \brief This methods notifies the platform, that a view has been detached + * from the related layout. + */ + virtual void detachView (View *view) = 0; + + /* + * ----------------------------------- + * Platform dependent properties + * ----------------------------------- + */ + + /** + * \brief Return the width of a text, with a given length and font. + */ + virtual int textWidth (style::Font *font, const char *text, int len) = 0; + + /** + * \brief Return the string resulting from transforming text to uppercase. + */ + virtual char *textToUpper (const char *text, int len) = 0; + + /** + * \brief Return the string resulting from transforming text to lowercase. + */ + virtual char *textToLower (const char *text, int len) = 0; + + /** + * \brief Return the index of the next glyph in string text. + */ + virtual int nextGlyph (const char *text, int idx) = 0; + + /** + * \brief Return the index of the previous glyph in string text. + */ + virtual int prevGlyph (const char *text, int idx) = 0; + + /** + * \brief Return screen resolution in x-direction. + */ + virtual float dpiX () = 0; + + /** + * \brief Return screen resolution in y-direction. + */ + virtual float dpiY () = 0; + + /* + * --------------------------------------------------------- + * These are to encapsulate some platform dependencies + * --------------------------------------------------------- + */ + + /** + * \brief Add an idle function. + * + * An idle function is called once, when no other + * tasks are to be done (e.g. there are no events to process), and then + * removed from the queue. The return value is a number, which can be + * used in removeIdle below. + */ + virtual int addIdle (void (Layout::*func) ()) = 0; + + /** + * \brief Remove an idle function, which has not been processed yet. + */ + virtual void removeIdle (int idleId) = 0; + + /* + * --------------------- + * Style Resources + * --------------------- + */ + + /** + * \brief Create a (platform dependent) font. + * + * Typically, within a platform, a sub class of dw::core::style::Font + * is defined, which holds more platform dependent data. + * + * Also, this method must fill the attributes "font" (when needed), + * "ascent", "descent", "spaceSidth" and "xHeight". If "tryEverything" + * is true, several methods should be used to use another font, when + * the requested font is not available. Passing false is typically done, + * if the caller wants to test different variations. + */ + virtual style::Font *createFont (style::FontAttrs *attrs, + bool tryEverything) = 0; + + virtual bool fontExists (const char *name) = 0; + + /** + * \brief Create a color resource for a given 0xrrggbb value. + */ + virtual style::Color *createColor (int color) = 0; + + /** + * \brief Create a tooltip + */ + virtual style::Tooltip *createTooltip (const char *text) = 0; + + /** + * \brief Cancel a tooltip (either shown or requested) + */ + virtual void cancelTooltip () = 0; + + /** + * \brief Create a (platform speficic) image buffer. + * + * "gamma" is the value by which the image data is gamma-encoded. + */ + virtual Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height, + double gamma) = 0; + + /** + * \brief Copy selected text (0-terminated). + */ + virtual void copySelection(const char *text) = 0; + + /** + * ... + */ + virtual ui::ResourceFactory *getResourceFactory () = 0; +}; + +} // namespace core +} // namespace dw + +#endif // __DW_PLATFORM_HH__ diff --git a/dw/preview.xbm b/dw/preview.xbm new file mode 100644 index 0000000..85ea829 --- /dev/null +++ b/dw/preview.xbm @@ -0,0 +1,5 @@ +#define preview_width 11 +#define preview_height 11 +static unsigned char preview_bits[] = { + 0x20, 0x00, 0x70, 0x00, 0x20, 0x00, 0x20, 0x00, 0x22, 0x02, 0xff, 0x07, + 0x22, 0x02, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x20, 0x00}; diff --git a/dw/selection.cc b/dw/selection.cc new file mode 100644 index 0000000..f5c7bda --- /dev/null +++ b/dw/selection.cc @@ -0,0 +1,494 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include "../lout/debug.hh" + +#include <string.h> + +using namespace lout; + +/* + * strndup() is a GNU extension. + */ +extern "C" char *strndup(const char *s, size_t size) +{ + char *r = (char *) malloc (size + 1); + + if (r) { + strncpy (r, s, size); + r[size] = 0; + } + + return r; +} + +namespace dw { +namespace core { + +SelectionState::SelectionState () +{ + DBG_OBJ_CREATE ("dw::core::SelectionState"); + + layout = NULL; + + selectionState = NONE; + from = NULL; + to = NULL; + + linkState = LINK_NONE; + link = NULL; +} + +SelectionState::~SelectionState () +{ + reset (); + DBG_OBJ_DELETE (); +} + +void SelectionState::reset () +{ + resetSelection (); + resetLink (); +} + +void SelectionState::resetSelection () +{ + if (from) + delete from; + from = NULL; + if (to) + delete to; + to = NULL; + selectionState = NONE; +} + + +void SelectionState::resetLink () +{ + if (link) + delete link; + link = NULL; + linkState = LINK_NONE; +} + +bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, + EventButton *event) +{ + Widget *itWidget = it->getWidget (); + bool ret = false; + + if (!event) return ret; + + if (event->button == 3) { + // menu popup + layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); + ret = true; + } else if (linkNo != -1) { + // link handling + (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); + resetLink (); + linkState = LINK_PRESSED; + linkButton = event->button; + DeepIterator *newLink = new DeepIterator (it); + if (newLink->isEmpty ()) { + delete newLink; + resetLink (); + } else { + link = newLink; + // It may be that the user has pressed on something activatable + // (linkNo != -1), but there is no contents, e.g. with images + // without ALTernative text. + if (link) { + linkChar = correctCharPos (link, charPos); + linkNumber = linkNo; + } + } + // We do not return the value of the signal method, + // but we do actually process this event. + ret = true; + } else if (event->button == 1) { + // normal selection handling + highlight (false, 0); + resetSelection (); + + selectionState = SELECTING; + DeepIterator *newFrom = new DeepIterator (it); + if (newFrom->isEmpty ()) { + delete newFrom; + resetSelection (); + } else { + from = newFrom; + fromChar = correctCharPos (from, charPos); + to = from->cloneDeepIterator (); + toChar = correctCharPos (to, charPos); + } + ret = true; + } + + return ret; +} + +bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, + EventButton *event) +{ + Widget *itWidget = it->getWidget (); + bool ret = false; + + if (linkState == LINK_PRESSED && event && event->button == linkButton) { + // link handling + ret = true; + if (linkNo != -1) + (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event); + + // The link where the user clicked the mouse button? + if (linkNo == linkNumber) { + resetLink (); + (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event); + } else { + if (event->button == 1) + // Reset links and switch to selection mode. The selection + // state will be set to SELECTING, which is handled some lines + // below. + switchLinkToSelection (it, charPos); + } + } + + if (selectionState == SELECTING && event && event->button == 1) { + // normal selection + ret = true; + adjustSelection (it, charPos); + + if (from->compareTo (to) == 0 && fromChar == toChar) + // nothing selected + resetSelection (); + else { + copy (); + selectionState = SELECTED; + } + } + + return ret; +} + +bool SelectionState::buttonMotion (Iterator *it, int charPos, int linkNo, + EventMotion *event) +{ + if (linkState == LINK_PRESSED) { + //link handling + if (linkNo != linkNumber) + // No longer the link where the user clicked the mouse button. + // Reset links and switch to selection mode. + switchLinkToSelection (it, charPos); + // Still in link: do nothing. + } else if (selectionState == SELECTING) { + // selection + adjustSelection (it, charPos); + } + + return true; +} + +/** + * \brief General form of dw::core::SelectionState::buttonPress, + * dw::core::SelectionState::buttonRelease and + * dw::core::SelectionState::buttonMotion. + */ +bool SelectionState::handleEvent (EventType eventType, Iterator *it, + int charPos, int linkNo, + MousePositionEvent *event) +{ + switch (eventType) { + case BUTTON_PRESS: + return buttonPress (it, charPos, linkNo, (EventButton*)event); + + case BUTTON_RELEASE: + return buttonRelease (it, charPos, linkNo, (EventButton*)event); + + case BUTTON_MOTION: + return buttonMotion (it, charPos, linkNo, (EventMotion*)event); + + + default: + misc::assertNotReached (); + } + + return false; +} + + +/** + * \brief This method is called when the user decides not to activate a link, + * but instead select text. + */ +void SelectionState::switchLinkToSelection (Iterator *it, int charPos) +{ + // It may be that selection->link is NULL, see a_Selection_button_press. + if (link) { + // Reset old selection. + highlight (false, 0); + resetSelection (); + + // Transfer link state into selection state. + from = link->cloneDeepIterator (); + fromChar = linkChar; + to = from->createVariant (it); + toChar = correctCharPos (to, charPos); + selectionState = SELECTING; + + // Reset link status. + resetLink (); + + highlight (true, 0); + + } else { + // A link was pressed on, but there is nothing to select. Reset + // everything. + resetSelection (); + resetLink (); + } +} + +/** + * \brief This method is used by core::dw::SelectionState::buttonMotion and + * core::dw::SelectionState::buttonRelease, and changes the second limit of + * the already existing selection region. + */ +void SelectionState::adjustSelection (Iterator *it, int charPos) +{ + DeepIterator *newTo; + int newToChar, cmpOld, cmpNew, cmpDiff, len; + bool bruteHighlighting = false; + + newTo = to->createVariant (it); + newToChar = correctCharPos (newTo, charPos); + + cmpOld = to->compareTo (from); + cmpNew = newTo->compareTo (from); + + if (cmpOld == 0 || cmpNew == 0) { + // Either before, or now, the limits differ only by the character + // position. + bruteHighlighting = true; + } else if (cmpOld * cmpNew < 0) { + // The selection order has changed, i.e. the user moved the selection + // end again beyond the position he started. + bruteHighlighting = true; + } else { + // Here, cmpOld and cmpNew are equivalent and != 0. + cmpDiff = newTo->compareTo (to); + + if (cmpOld * cmpDiff > 0) { + // The user has enlarged the selection. Highlight the difference. + if (cmpDiff < 0) { + len = correctCharPos (to, END_OF_WORD); + highlight0 (true, newTo, newToChar, to, len + 1, 1); + } else { + highlight0 (true, to, 0, newTo, newToChar, -1); + } + } else { + if (cmpOld * cmpDiff < 0) { + // The user has reduced the selection. Unhighlight the difference. + highlight0 (false, to, 0, newTo, 0, cmpDiff); + } + + // Otherwise, the user has changed the position only slightly. + // In both cases, re-highlight the new position. + if (cmpOld < 0) { + len = correctCharPos (newTo, END_OF_WORD); + newTo->highlight (newToChar, len + 1, HIGHLIGHT_SELECTION); + } else + newTo->highlight (0, newToChar, HIGHLIGHT_SELECTION); + } + } + + if (bruteHighlighting) + highlight (false, 0); + + delete to; + to = newTo; + toChar = newToChar; + + if (bruteHighlighting) + highlight (true, 0); +} + +/** + * \brief This method deals especially with the case that a widget passes + * dw::core::SelectionState::END_OF_WORD. + */ +int SelectionState::correctCharPos (DeepIterator *it, int charPos) +{ + Iterator *top = it->getTopIterator (); + int len; + + if (top->getContent()->type == Content::TEXT) + len = strlen(top->getContent()->text); + else + len = 1; + + return misc::min(charPos, len); +} + +void SelectionState::highlight0 (bool fl, DeepIterator *from, int fromChar, + DeepIterator *to, int toChar, int dir) +{ + DeepIterator *a, *b, *i; + int cmp, aChar, bChar; + bool start; + + if (from && to) { + cmp = from->compareTo (to); + if (cmp == 0) { + if (fl) { + if (fromChar < toChar) + from->highlight (fromChar, toChar, HIGHLIGHT_SELECTION); + else + from->highlight (toChar, fromChar, HIGHLIGHT_SELECTION); + } else + from->unhighlight (0, HIGHLIGHT_SELECTION); + return; + } + + if (cmp < 0) { + a = from; + aChar = fromChar; + b = to; + bChar = toChar; + } else { + a = to; + aChar = toChar; + b = from; + bChar = fromChar; + } + + for (i = a->cloneDeepIterator (), start = true; + (cmp = i->compareTo (b)) <= 0; + i->next (), start = false) { + if (i->getContent()->type == Content::TEXT) { + if (fl) { + if (start) { + i->highlight (aChar, strlen (i->getContent()->text) + 1, + HIGHLIGHT_SELECTION); + } else if (cmp == 0) { + // the end + i->highlight (0, bChar, HIGHLIGHT_SELECTION); + } else { + i->highlight (0, strlen (i->getContent()->text) + 1, + HIGHLIGHT_SELECTION); + } + } else { + i->unhighlight (dir, HIGHLIGHT_SELECTION); + } + } + } + delete i; + } +} + +void SelectionState::copy() +{ + if (from && to) { + Iterator *si; + DeepIterator *a, *b, *i; + int cmp, aChar, bChar; + bool start; + char *tmp; + misc::StringBuffer strbuf; + + cmp = from->compareTo (to); + if (cmp == 0) { + if (from->getContent()->type == Content::TEXT) { + si = from->getTopIterator (); + if (fromChar < toChar) + tmp = strndup (si->getContent()->text + fromChar, + toChar - fromChar); + else + tmp = strndup (si->getContent()->text + toChar, + fromChar - toChar); + strbuf.appendNoCopy (tmp); + } + } else { + if (cmp < 0) { + a = from; + aChar = fromChar; + b = to; + bChar = toChar; + } else { + a = to; + aChar = toChar; + b = from; + bChar = fromChar; + } + + for (i = a->cloneDeepIterator (), start = true; + (cmp = i->compareTo (b)) <= 0; + i->next (), start = false) { + si = i->getTopIterator (); + switch (si->getContent()->type) { + case Content::TEXT: + if (start) { + tmp = strndup (si->getContent()->text + aChar, + strlen (i->getContent()->text) - aChar); + strbuf.appendNoCopy (tmp); + } else if (cmp == 0) { + // the end + tmp = strndup (si->getContent()->text, bChar); + strbuf.appendNoCopy (tmp); + } else + strbuf.append (si->getContent()->text); + + if (si->getContent()->space && cmp != 0) + strbuf.append (" "); + + break; + + case Content::BREAK: + if (si->getContent()->breakSpace > 0) + strbuf.append ("\n\n"); + else + strbuf.append ("\n"); + break; + default: + // Make pedantic compilers happy. Especially + // DW_CONTENT_WIDGET is never returned by a DwDeepIterator. + break; + } + } + delete i; + } + + layout->copySelection(strbuf.getChars()); + } +} + +} // namespace core +} // namespace dw diff --git a/dw/selection.hh b/dw/selection.hh new file mode 100644 index 0000000..ef9df0e --- /dev/null +++ b/dw/selection.hh @@ -0,0 +1,241 @@ +#ifndef __DW_SELECTION_H__ +#define __DW_SELECTION_H__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief This class handles selections, as well as activation of links, + * which is closely related. + * + * <h3>General Overview</h3> + * + * dw::core::SelectionState is associated with dw::core::Layout. The selection + * state is controlled by "abstract events", which are sent by single + * widgets by calling one of the following methods: + * + * <ul> + * <li> dw::core::SelectionState::buttonPress for button press events, + * <li> dw::core::SelectionState::buttonRelease for button release events, and + * <li> dw::core::SelectionState::buttonMotion for motion events (with pressed + * mouse button). + * </ul> + * + * The widget must construct simple iterators (dw::core::Iterator), which will + * be transferred to deep iterators (dw::core::DeepIterator), see below for + * more details. All event handling methods have the same signature, the + * arguments in detail are: + * + * <table> + * <tr><td>dw::core::Iterator *it <td>the iterator pointing on the item + * under the mouse pointer; this + * iterator \em must be created with + * dw::core::Content::SELECTION_CONTENT + * as mask + * <tr><td>int charPos <td>the exact (character) position + * within the iterator, + * <tr><td>int linkNo <td>if this item is associated with a + * link, its number (see + * dw::core::Layout::LinkReceiver), + * otherwise -1 + * <tr><td>dw::core::EventButton *event <td>the event itself; only the button + * is used + * </table> + * + * Look also at dw::core::SelectionState::handleEvent, which may be useful + * in some circumstances. + * + * In some cases, \em charPos would be difficult to determine. E.g., when + * the dw::Textblock widget decides that the user is pointing on a position + * <i>at the end</i> of an image (DwImage), it constructs a simple iterator + * pointing on this image widget. In a simple iterator, that fact that + * the pointer is at the end, would be represented by \em charPos == 1. But + * when transferring this simple iterator into an deep iterator, this + * simple iterator is discarded and instead the stack has an iterator + * pointing to text at the top. As a result, only the first letter of the + * ALT text would be copied. + * + * To avoid this problem, widgets should in this case pass + * dw::core::SelectionState::END_OF_WORD as \em charPos, which is then + * automatically reduced to the actual length of the deep(!) iterator. + * + * The return value is the same as in DwWidget event handling methods. + * I.e., in most cases, they should simply return it. The events + * dw::core::Layout::LinkReceiver::press, + * dw::core::Layout::LinkReceiver::release and + * dw::core::Layout::LinkReceiver::click (but not + * dw::core::Layout::LinkReceiver::enter) are emitted by these methods, so + * that widgets which let dw::core::SelectionState handle links, should only + * emit dw::core::Layout::LinkReceiver::enter for themselves. + * + * <h3>Selection State</h3> + * + * Selection interferes with handling the activation of links, so the + * latter is also handled by the dw::core::SelectionState. Details are based on + * following guidelines: + * + * <ol> + * <li> It should be simple to select links and to start selection in + * links. The rule to distinguish between link activation and + * selection is that the selection starts as soon as the user leaves + * the link. (This is, IMO, a useful feature. Even after drag and + * drop has been implemented in dillo, this should be somehow + * preserved.) + * + * <li> The selection should stay as long as possible, i.e., the old + * selection is only cleared when a new selection is started. + * </ol> + * + * The latter leads to a model with two states: the selection state and + * the link handling state. + * + * The general selection works, for events not pointing on links, like + * this (numbers in parantheses after the event denote the button, "n" + * means arbitrary button): + * + * \dot + * digraph G { + * node [shape=ellipse, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, + * color="#404040", labelfontcolor="#000080", + * fontname=Helvetica, fontsize=10, fontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * NONE; + * SELECTING; + * q [label="Anything selected?", shape=plaintext]; + * SELECTED; + * + * NONE -> SELECTING [label="press(1)\non non-link"]; + * SELECTING -> SELECTING [label="motion(1)"]; + * SELECTING -> q [label="release(1)"]; + * q -> SELECTED [label="yes"]; + * q -> NONE [label="no"]; + * SELECTED -> SELECTING [label="press(1)"]; + * + * } + * \enddot + * + * The selected region is represented by two instances of + * dw::core::DeepIterator. + * + * Links are handled by a different state machine: + * + * \dot + * digraph G { + * node [shape=ellipse, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, + * color="#404040", labelfontcolor="#000080", + * fontname=Helvetica, fontsize=10, fontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * LINK_NONE; + * LINK_PRESSED; + * click [label="Emit \"click\" signal.", shape=record]; + * q11 [label="Still the same link?", shape=plaintext]; + * q21 [label="Still the same link?", shape=plaintext]; + * q22 [label="n == 1?", shape=plaintext]; + * SELECTED [label="Switch selection\nto SELECTED", shape=record]; + * q12 [label="n == 1?", shape=plaintext]; + * SELECTING [label="Switch selection\nto SELECTING", shape=record]; + * + * LINK_NONE -> LINK_PRESSED [label="press(n)\non link"]; + * LINK_PRESSED -> q11 [label="motion(n)"]; + * q11 -> LINK_PRESSED [label="yes"]; + * q11 -> q12 [label="no"]; + * q12 -> SELECTING [label="yes"]; + * SELECTING -> LINK_NONE; + * q12 -> LINK_NONE [label="no"]; + * LINK_PRESSED -> q21 [label="release(n)"]; + * q21 -> click [label="yes"]; + * click -> LINK_NONE; + * q21 -> q22 [label="no"]; + * q22 -> SELECTED [label="yes"]; + * SELECTED -> LINK_NONE; + * q22 -> LINK_NONE [label="no"]; + * } + * \enddot + * + * Switching selection simply means that the selection state will + * eventually be SELECTED/SELECTING, with the original and the current + * position making up the selection region. This happens for button 1, + * events with buttons other than 1 do not affect selection at all. + * + * + * \todo dw::core::SelectionState::buttonMotion currently always assumes + * that button 1 has been pressed (since otherwise it would not do + * anything). This should be made a bit cleaner. + * + * \todo The selection should be cleared, when the user selects something + * somewhere else (perhaps switched into "non-active" mode, as e.g. Gtk+ + * does). + * + */ +class SelectionState +{ +public: + enum { END_OF_WORD = 1 << 30 }; + +private: + Layout *layout; + + // selection + enum { + NONE, + SELECTING, + SELECTED + } selectionState; + + DeepIterator *from, *to; + int fromChar, toChar; + + // link handling + enum { + LINK_NONE, + LINK_PRESSED + } linkState; + + int linkButton; + DeepIterator *link; + int linkChar, linkNumber; + + void resetSelection (); + void resetLink (); + void switchLinkToSelection (Iterator *it, int charPos); + void adjustSelection (Iterator *it, int charPos); + static int correctCharPos (DeepIterator *it, int charPos); + + void highlight (bool fl, int dir) + { highlight0 (fl, from, fromChar, to, toChar, dir); } + + void highlight0 (bool fl, DeepIterator *from, int fromChar, + DeepIterator *to, int toChar, int dir); + void copy (); + +public: + enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION }; + + SelectionState (); + ~SelectionState (); + + inline void setLayout (Layout *layout) { this->layout = layout; } + void reset (); + bool buttonPress (Iterator *it, int charPos, int linkNo, + EventButton *event); + bool buttonRelease (Iterator *it, int charPos, int linkNo, + EventButton *event); + bool buttonMotion (Iterator *it, int charPos, int linkNo, + EventMotion *event); + + bool handleEvent (EventType eventType, Iterator *it, int charPos, + int linkNo, MousePositionEvent *event); +}; + +} // namespace core +} // namespace dw + +#endif // __DW_SELECTION_H__ diff --git a/dw/style.cc b/dw/style.cc new file mode 100644 index 0000000..aaeb959 --- /dev/null +++ b/dw/style.cc @@ -0,0 +1,1468 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <math.h> + +#include "core.hh" +#include "../lout/msg.h" + +using namespace lout; + +namespace dw { +namespace core { +namespace style { + +const bool drawBackgroundLineByLine = false; + +const int MIN_BG_IMG_W = 10; +const int MIN_BG_IMG_H = 10; +const int OPT_BG_IMG_W = 50; +const int OPT_BG_IMG_H = 50; + +static void calcBackgroundRelatedValues (StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment + backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int xDraw, int yDraw, int widthDraw, + int heightDraw, int xRef, int yRef, + int widthRef, int heightRef, + bool *repeatX, bool *repeatY, + int *origX, int *origY, + int *tileX1, int *tileX2, int *tileY1, + int *tileY2, bool *doDraw); + +void StyleAttrs::initValues () +{ + x_link = -1; + x_lang[0] = x_lang[1] = 0; + x_img = -1; + x_tooltip = NULL; + textDecoration = TEXT_DECORATION_NONE; + textAlign = TEXT_ALIGN_LEFT; + textAlignChar = '.'; + textTransform = TEXT_TRANSFORM_NONE; + listStylePosition = LIST_STYLE_POSITION_OUTSIDE; + listStyleType = LIST_STYLE_TYPE_DISC; + valign = VALIGN_BASELINE; + backgroundColor = NULL; + backgroundImage = NULL; + backgroundRepeat = BACKGROUND_REPEAT; + backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL; + backgroundPositionX = createPerLength (0); + backgroundPositionY = createPerLength (0); + width = height = lineHeight = LENGTH_AUTO; + minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO; + vloat = FLOAT_NONE; + clear = CLEAR_NONE; + overflow = OVERFLOW_VISIBLE; + position = POSITION_STATIC; + top = bottom = left = right = LENGTH_AUTO; + textIndent = 0; + margin.setVal (0); + borderWidth.setVal (0); + padding.setVal (0); + borderCollapse = BORDER_MODEL_SEPARATE; + setBorderColor (NULL); + setBorderStyle (BORDER_NONE); + hBorderSpacing = 0; + vBorderSpacing = 0; + wordSpacing = 0; + + display = DISPLAY_INLINE; + whiteSpace = WHITE_SPACE_NORMAL; + cursor = CURSOR_DEFAULT; +} + +/** + * \brief Reset those style attributes to their standard values, which are + * not inherited, according to CSS. + */ +void StyleAttrs::resetValues () +{ + x_img = -1; + + valign = VALIGN_BASELINE; + textAlignChar = '.'; + vloat = FLOAT_NONE; /** \todo Correct? Check specification. */ + clear = CLEAR_NONE; /** \todo Correct? Check specification. */ + overflow = OVERFLOW_VISIBLE; + position = POSITION_STATIC; /** \todo Correct? Check specification. */ + top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check + specification. */ + backgroundColor = NULL; + backgroundImage = NULL; + backgroundRepeat = BACKGROUND_REPEAT; + backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL; + backgroundPositionX = createPerLength (0); + backgroundPositionY = createPerLength (0); + width = LENGTH_AUTO; + height = LENGTH_AUTO; + minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO; + + margin.setVal (0); + borderWidth.setVal (0); + padding.setVal (0); + setBorderColor (NULL); + setBorderStyle (BORDER_NONE); + hBorderSpacing = 0; + vBorderSpacing = 0; + + display = DISPLAY_INLINE; +} + +/** + * \brief This method returns whether something may change its size, when + * its style changes from this style to \em otherStyle. + * + * It is mainly for optimizing style changes where only colors etc change + * (where false would be returned), in some cases it may return true, although + * a size change does not actually happen (e.g. when in a certain + * context a particular attribute is ignored). + * + * \todo Should for CSS implemented properly. Currently, size changes are + * not needed, so always false is returned. See also + * dw::core::Widget::setStyle. + */ +bool StyleAttrs::sizeDiffs (StyleAttrs *otherStyle) +{ + return false; +} + +bool StyleAttrs::equals (object::Object *other) { + StyleAttrs *otherAttrs = (StyleAttrs *) other; + + return this == otherAttrs || + (font == otherAttrs->font && + textDecoration == otherAttrs->textDecoration && + color == otherAttrs->color && + backgroundColor == otherAttrs->backgroundColor && + backgroundImage == otherAttrs->backgroundImage && + backgroundRepeat == otherAttrs->backgroundRepeat && + backgroundAttachment == otherAttrs->backgroundAttachment && + backgroundPositionX == otherAttrs->backgroundPositionX && + backgroundPositionY == otherAttrs->backgroundPositionY && + textAlign == otherAttrs->textAlign && + valign == otherAttrs->valign && + textAlignChar == otherAttrs->textAlignChar && + textTransform == otherAttrs->textTransform && + vloat == otherAttrs->vloat && + clear == otherAttrs->clear && + overflow == otherAttrs->overflow && + position == otherAttrs->position && + top == otherAttrs->top && + bottom == otherAttrs->bottom && + left == otherAttrs->left && + right == otherAttrs->right && + hBorderSpacing == otherAttrs->hBorderSpacing && + vBorderSpacing == otherAttrs->vBorderSpacing && + wordSpacing == otherAttrs->wordSpacing && + width == otherAttrs->width && + height == otherAttrs->height && + minWidth == otherAttrs->minWidth && + maxWidth == otherAttrs->maxWidth && + minHeight == otherAttrs->minHeight && + maxHeight == otherAttrs->maxHeight && + lineHeight == otherAttrs->lineHeight && + textIndent == otherAttrs->textIndent && + margin.equals (&otherAttrs->margin) && + borderWidth.equals (&otherAttrs->borderWidth) && + padding.equals (&otherAttrs->padding) && + borderCollapse == otherAttrs->borderCollapse && + borderColor.top == otherAttrs->borderColor.top && + borderColor.right == otherAttrs->borderColor.right && + borderColor.bottom == otherAttrs->borderColor.bottom && + borderColor.left == otherAttrs->borderColor.left && + borderStyle.top == otherAttrs->borderStyle.top && + borderStyle.right == otherAttrs->borderStyle.right && + borderStyle.bottom == otherAttrs->borderStyle.bottom && + borderStyle.left == otherAttrs->borderStyle.left && + display == otherAttrs->display && + whiteSpace == otherAttrs->whiteSpace && + listStylePosition == otherAttrs->listStylePosition && + listStyleType == otherAttrs->listStyleType && + cursor == otherAttrs->cursor && + x_link == otherAttrs->x_link && + x_lang[0] == otherAttrs->x_lang[0] && + x_lang[1] == otherAttrs->x_lang[1] && + x_img == otherAttrs->x_img && + x_tooltip == otherAttrs->x_tooltip); +} + +int StyleAttrs::hashValue () { + return (intptr_t) font + + textDecoration + + (intptr_t) color + + (intptr_t) backgroundColor + + (intptr_t) backgroundImage + + backgroundRepeat + + backgroundAttachment + + backgroundPositionX + + backgroundPositionY + + textAlign + + valign + + textAlignChar + + textTransform + + vloat + + clear + + overflow + + position + + top + + bottom + + left + + right + + hBorderSpacing + + vBorderSpacing + + wordSpacing + + width + + height + + minWidth + + maxWidth + + minHeight + + maxHeight + + lineHeight + + textIndent + + margin.hashValue () + + borderWidth.hashValue () + + padding.hashValue () + + borderCollapse + + (intptr_t) borderColor.top + + (intptr_t) borderColor.right + + (intptr_t) borderColor.bottom + + (intptr_t) borderColor.left + + borderStyle.top + + borderStyle.right + + borderStyle.bottom + + borderStyle.left + + display + + whiteSpace + + listStylePosition + + listStyleType + + cursor + + x_link + + x_lang[0] + x_lang[1] + + x_img + + (intptr_t) x_tooltip; +} + +int Style::totalRef = 0; +container::typed::HashTable <StyleAttrs, Style> * Style::styleTable = + new container::typed::HashTable <StyleAttrs, Style> (false, false, 1024); + +Style::Style (StyleAttrs *attrs) +{ + DBG_OBJ_CREATE ("dw::core::style::Style"); + + copyAttrs (attrs); + + DBG_OBJ_ASSOC_CHILD (font); + DBG_OBJ_ASSOC_CHILD (color); + DBG_OBJ_ASSOC_CHILD (backgroundColor); + DBG_OBJ_ASSOC_CHILD (backgroundImage); + DBG_OBJ_ASSOC_CHILD (borderColor.top); + DBG_OBJ_ASSOC_CHILD (borderColor.bottom); + DBG_OBJ_ASSOC_CHILD (borderColor.left); + DBG_OBJ_ASSOC_CHILD (borderColor.right); + //DBG_OBJ_ASSOC_CHILD (x_tooltip); + + refCount = 1; + + font->ref (); + if (color) + color->ref (); + if (backgroundColor) + backgroundColor->ref (); + if (backgroundImage) + backgroundImage->ref (); + if (borderColor.top) + borderColor.top->ref(); + if (borderColor.bottom) + borderColor.bottom->ref(); + if (borderColor.left) + borderColor.left->ref(); + if (borderColor.right) + borderColor.right->ref(); + if (x_tooltip) + x_tooltip->ref(); + + totalRef++; +} + +Style::~Style () +{ + font->unref (); + + if (color) + color->unref (); + if (backgroundColor) + backgroundColor->unref (); + if (backgroundImage) + backgroundImage->unref (); + if (borderColor.top) + borderColor.top->unref(); + if (borderColor.bottom) + borderColor.bottom->unref(); + if (borderColor.left) + borderColor.left->unref(); + if (borderColor.right) + borderColor.right->unref(); + if (x_tooltip) + x_tooltip->unref(); + + styleTable->remove (this); + totalRef--; + + DBG_OBJ_DELETE (); +} + +void Style::copyAttrs (StyleAttrs *attrs) +{ + font = attrs->font; + textDecoration = attrs->textDecoration; + color = attrs->color; + backgroundColor = attrs->backgroundColor; + backgroundImage = attrs->backgroundImage; + backgroundRepeat = attrs->backgroundRepeat; + backgroundAttachment = attrs->backgroundAttachment; + backgroundPositionX = attrs->backgroundPositionX; + backgroundPositionY = attrs->backgroundPositionY; + textAlign = attrs->textAlign; + valign = attrs->valign; + textAlignChar = attrs->textAlignChar; + textTransform = attrs->textTransform; + vloat = attrs->vloat; + clear = attrs->clear; + overflow = attrs->overflow; + position = attrs->position; + top = attrs->top; + bottom = attrs->bottom; + left = attrs->left; + right = attrs->right; + hBorderSpacing = attrs->hBorderSpacing; + vBorderSpacing = attrs->vBorderSpacing; + wordSpacing = attrs->wordSpacing; + width = attrs->width; + height = attrs->height; + lineHeight = attrs->lineHeight; + textIndent = attrs->textIndent; + minWidth = attrs->minWidth; + maxWidth = attrs->maxWidth; + minHeight = attrs->minHeight; + maxHeight = attrs->maxHeight; + margin = attrs->margin; + borderWidth = attrs->borderWidth; + padding = attrs->padding; + borderCollapse = attrs->borderCollapse; + borderColor = attrs->borderColor; + borderStyle = attrs->borderStyle; + display = attrs->display; + whiteSpace = attrs->whiteSpace; + listStylePosition = attrs->listStylePosition; + listStyleType = attrs->listStyleType; + cursor = attrs->cursor; + x_link = attrs->x_link; + x_lang[0] = attrs->x_lang[0]; + x_lang[1] = attrs->x_lang[1]; + x_img = attrs->x_img; + x_tooltip = attrs->x_tooltip; +} + +// ---------------------------------------------------------------------- + +bool FontAttrs::equals(object::Object *other) +{ + FontAttrs *otherAttrs = (FontAttrs*)other; + return + this == otherAttrs || + (size == otherAttrs->size && + weight == otherAttrs->weight && + style == otherAttrs->style && + letterSpacing == otherAttrs->letterSpacing && + fontVariant == otherAttrs->fontVariant && + strcmp (name, otherAttrs->name) == 0); +} + +int FontAttrs::hashValue() +{ + int h = object::String::hashValue (name); + h = (h << 5) - h + size; + h = (h << 5) - h + weight; + h = (h << 5) - h + style; + h = (h << 5) - h + letterSpacing; + h = (h << 5) - h + fontVariant; + return h; +} + +Font::~Font () +{ + free ((char*)name); + DBG_OBJ_DELETE (); +} + +void Font::copyAttrs (FontAttrs *attrs) +{ + name = strdup (attrs->name); + size = attrs->size; + weight = attrs->weight; + style = attrs->style; + letterSpacing = attrs->letterSpacing; + fontVariant = attrs->fontVariant; +} + +Font *Font::create0 (Layout *layout, FontAttrs *attrs, + bool tryEverything) +{ + return layout->createFont (attrs, tryEverything); +} + +Font *Font::create (Layout *layout, FontAttrs *attrs) +{ + return create0 (layout, attrs, false); +} + +bool Font::exists (Layout *layout, const char *name) +{ + return layout->fontExists (name); +} + +// ---------------------------------------------------------------------- + +bool ColorAttrs::equals(object::Object *other) +{ + ColorAttrs *oc = (ColorAttrs*)other; + return this == oc || (color == oc->color); +} + +int ColorAttrs::hashValue() +{ + return color; +} + +Color::~Color () +{ + DBG_OBJ_DELETE (); +} + +int Color::shadeColor (int color, int d) +{ + int red = (color >> 16) & 255; + int green = (color >> 8) & 255; + int blue = color & 255; + + double oldLightness = ((double) misc::max (red, green, blue)) / 255; + double newLightness; + + if (oldLightness > 0.8) { + if (d > 0) + newLightness = oldLightness - 0.2; + else + newLightness = oldLightness - 0.4; + } else if (oldLightness < 0.2) { + if (d > 0) + newLightness = oldLightness + 0.4; + else + newLightness = oldLightness + 0.2; + } else + newLightness = oldLightness + d * 0.2; + + if (oldLightness) { + double f = (newLightness / oldLightness); + red = (int)(red * f); + green = (int)(green * f); + blue = (int)(blue * f); + } else { + red = green = blue = (int)(newLightness * 255); + } + + return (red << 16) | (green << 8) | blue; +} + +int Color::shadeColor (int color, Shading shading) +{ + switch (shading) { + case SHADING_NORMAL: + return color; + + case SHADING_LIGHT: + return shadeColor(color, +1); + + case SHADING_INVERSE: + return color ^ 0xffffff; + + case SHADING_DARK: + return shadeColor(color, -1); + + default: + // compiler happiness + misc::assertNotReached (); + return -1; + } +} + + +Color *Color::create (Layout *layout, int col) +{ + ColorAttrs attrs(col); + + return layout->createColor (col); +} + +Tooltip *Tooltip::create (Layout *layout, const char *text) +{ + return layout->createTooltip (text); +} + +// ---------------------------------------------------------------------- + +void StyleImage::StyleImgRenderer::setBuffer (core::Imgbuf *buffer, bool resize) +{ + if (image->imgbufSrc) + image->imgbufSrc->unref (); + if (image->imgbufTiled) + image->imgbufTiled->unref (); + + image->imgbufTiled = NULL; + + image->imgbufSrc = buffer; + DBG_OBJ_ASSOC (image, image->imgbufSrc); + + if (image->imgbufSrc) { + image->imgbufSrc->ref (); + + // If the image is too small, drawing a background will cause + // many calls of View::drawImgbuf. For this reason, we create + // another image buffer, the "tiled" image buffer, which is + // larger (the "optimal" size is defined as OPT_BG_IMG_W * + // OPT_BG_IMG_H) and contains the "source" buffer several times. + // + // This "tiled" buffer is not used when 'background-repeat' has + // another value than 'repeat', for obvious reasons. Image + // buffers only "tiled" in one dimension (to optimize 'repeat-x' + // and 'repeat-y') are not supported. + + if (image->imgbufSrc->getRootWidth() * image->imgbufSrc->getRootHeight() + < MIN_BG_IMG_W * MIN_BG_IMG_H) { + image->tilesX = + misc::max (OPT_BG_IMG_W / image->imgbufSrc->getRootWidth(), 1); + image->tilesY = + misc::max (OPT_BG_IMG_H / image->imgbufSrc->getRootHeight(), 1); + image->imgbufTiled = + image->imgbufSrc->createSimilarBuf + (image->tilesX * image->imgbufSrc->getRootWidth(), + image->tilesY * image->imgbufSrc->getRootHeight()); + + DBG_OBJ_ASSOC (image, image->imgbufTiled); + } + } +} + +void StyleImage::StyleImgRenderer::drawRow (int row) +{ + if (image->imgbufTiled) { + // A row of data has been copied to the source buffer, here it + // is copied into the tiled buffer. + + // Unfortunately, this code may be called *after* some other + // implementations of ImgRenderer::drawRow, which actually + // *draw* the tiled buffer, which is so not up to date + // (ImgRendererDist does not define an order). OTOH, these + // drawing implementations calle Widget::queueResize, so the + // actual drawing (and so access to the tiled buffer) is done + // later. + + int w = image->imgbufSrc->getRootWidth (); + int h = image->imgbufSrc->getRootHeight (); + + for (int x = 0; x < image->tilesX; x++) + for (int y = 0; y < image->tilesX; y++) + image->imgbufSrc->copyTo (image->imgbufTiled, x * w, y * h, + 0, row, w, 1); + } +} + +void StyleImage::StyleImgRenderer::finish () +{ + // Nothing to do. +} + +void StyleImage::StyleImgRenderer::fatal () +{ + // Nothing to do. +} + +StyleImage::StyleImage () +{ + DBG_OBJ_CREATE ("dw::core::style::StyleImage"); + + refCount = 0; + imgbufSrc = NULL; + imgbufTiled = NULL; + + imgRendererDist = new ImgRendererDist (); + styleImgRenderer = new StyleImgRenderer (this); + imgRendererDist->put (styleImgRenderer); +} + +StyleImage::~StyleImage () +{ + if (imgbufSrc) + imgbufSrc->unref (); + if (imgbufTiled) + imgbufTiled->unref (); + + delete imgRendererDist; + delete styleImgRenderer; + + DBG_OBJ_DELETE (); +} + +void StyleImage::ExternalImgRenderer::setBuffer (core::Imgbuf *buffer, + bool resize) +{ + // Nothing to do? +} + +void StyleImage::ExternalImgRenderer::drawRow (int row) +{ + if (drawBackgroundLineByLine) { + StyleImage *backgroundImage; + if (readyToDraw () && (backgroundImage = getBackgroundImage ())) { + // All single rows are drawn. + + Imgbuf *imgbuf = backgroundImage->getImgbufSrc(); + int imgWidth = imgbuf->getRootWidth (); + int imgHeight = imgbuf->getRootHeight (); + + int x, y, width, height; + getBgArea (&x, &y, &width, &height); + + int xRef, yRef, widthRef, heightRef; + getRefArea (&xRef, &yRef, &widthRef, &heightRef); + + bool repeatX, repeatY, doDraw; + int origX, origY, tileX1, tileX2, tileY1, tileY2; + + calcBackgroundRelatedValues (backgroundImage, + getBackgroundRepeat (), + getBackgroundAttachment (), + getBackgroundPositionX (), + getBackgroundPositionY (), + x, y, width, height, xRef, yRef, widthRef, + heightRef, &repeatX, &repeatY, &origX, + &origY, &tileX1, &tileX2, &tileY1, + &tileY2, &doDraw); + + //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n", + // tileX1, tileX2, tileY1, tileY2); + + if (doDraw) + // Only iterate over y, because the rows can be combined + // horizontally. + for (int tileY = tileY1; tileY <= tileY2; tileY++) { + int x1 = misc::max (origX + tileX1 * imgWidth, x); + int x2 = misc::min (origX + (tileX2 + 1) * imgWidth, x + width); + + int yt = origY + tileY * imgHeight + row; + if (yt >= y && yt < y + height) + draw (x1, yt, x2 - x1, 1); + } + } + } +} + +void StyleImage::ExternalImgRenderer::finish () +{ + if (!drawBackgroundLineByLine) { + if (readyToDraw ()) { + // Draw total area, as a whole. + int x, y, width, height; + getBgArea (&x, &y, &width, &height); + draw (x, y, width, height); + } + } +} + +void StyleImage::ExternalImgRenderer::fatal () +{ + // Nothing to do. +} + +// ---------------------------------------------------------------------- + +StyleImage *StyleImage::ExternalWidgetImgRenderer::getBackgroundImage () +{ + Style *style = getStyle (); + return style ? style->backgroundImage : NULL; +} + +BackgroundRepeat StyleImage::ExternalWidgetImgRenderer::getBackgroundRepeat () +{ + Style *style = getStyle (); + return style ? style->backgroundRepeat : BACKGROUND_REPEAT; +} + +BackgroundAttachment + StyleImage::ExternalWidgetImgRenderer::getBackgroundAttachment () +{ + Style *style = getStyle (); + return style ? style->backgroundAttachment : BACKGROUND_ATTACHMENT_SCROLL; +} + +Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionX () +{ + Style *style = getStyle (); + return style ? style->backgroundPositionX : createPerLength (0); +} + +Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionY () +{ + Style *style = getStyle (); + return style ? style->backgroundPositionY : createPerLength (0); +} + +// ---------------------------------------------------------------------- + +/* + * The drawBorder{Top,Bottom,Left,Right} functions are similar. They + * use a trapezium as draw polygon, or drawTypedLine() for dots and dashes. + * Although the concept is simple, achieving pixel accuracy is laborious [1]. + * + * [1] http://www.dillo.org/css_compat/tests/border-style.html + */ +static void drawBorderTop(View *view, Style *style, + int x1, int y1, int x2, int y2) + +{ + int d, w; + Point points[4]; + const bool filled = true, convex = true; + bool ridge = false, inset = false, dotted = false; + Color::Shading shading = Color::SHADING_NORMAL; + + if (!style->borderColor.top || style->borderWidth.top == 0) + return; + + switch (style->borderStyle.top) { + case BORDER_NONE: + case BORDER_HIDDEN: + break; + case BORDER_DOTTED: + dotted = true; + case BORDER_DASHED: + w = style->borderWidth.top; + view->drawTypedLine(style->borderColor.top, shading, + dotted ? LINE_DOTTED : LINE_DASHED, + w, x1+w/2, y1+w/2, x2-w/2, y2+w/2); + break; + case BORDER_SOLID: + case BORDER_INSET: + inset = true; + case BORDER_OUTSET: + if (style->borderStyle.top != BORDER_SOLID) + shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + + if (style->borderWidth.top == 1) { + view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2); + } else { + points[0].x = x1; + points[1].x = x2 + 1; + points[0].y = points[1].y = y1; + points[2].x = points[1].x - style->borderWidth.right; + points[3].x = x1 + style->borderWidth.left; + points[2].y = points[3].y = points[0].y + style->borderWidth.top; + view->drawPolygon (style->borderColor.top, shading, filled, convex, + points, 4); + } + break; + case BORDER_RIDGE: + ridge = true; + case BORDER_GROOVE: + d = style->borderWidth.top & 1; + points[0].x = x1; + points[1].x = x2 + 1; + points[0].y = points[1].y = y1; + points[2].x = x2 - style->borderWidth.right / 2; + points[3].x = x1 + style->borderWidth.left / 2; + points[2].y = points[3].y = y1 + style->borderWidth.top / 2 + d; + shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; + view->drawPolygon (style->borderColor.top, shading, filled, convex, + points, 4); + points[0].x = x1 + style->borderWidth.left / 2 + d; + points[1].x = x2 - style->borderWidth.right / 2 + 1 - d; + points[0].y = points[1].y = y1 + style->borderWidth.top / 2 + d; + points[2].x = x2 - style->borderWidth.right + 1 - d; + points[3].x = x1 + style->borderWidth.left; + points[2].y = points[3].y = y1 + style->borderWidth.top; + shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + view->drawPolygon (style->borderColor.top, shading, filled, convex, + points, 4); + break; + case BORDER_DOUBLE: + w = (int) rint(style->borderWidth.top / 3.0); + d = w ? style->borderWidth.top - 2 * w : 0; + int w_l = (int) rint(style->borderWidth.left / 3.0); + int w_r = (int) rint(style->borderWidth.right / 3.0); + if (style->borderWidth.top == 1) { + view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2); + break; + } + points[0].x = x1; + points[1].x = x2 + 1; + points[0].y = points[1].y = y1; + points[2].x = points[1].x - w_r; + points[3].x = points[0].x + w_l; + points[2].y = points[3].y = points[0].y + w; + view->drawPolygon (style->borderColor.top, shading, filled, convex, + points, 4); + points[0].x = x1 + style->borderWidth.left - w_l; + points[1].x = x2 + 1 - style->borderWidth.right + w_r; + points[0].y = points[1].y = y1 + w + d; + points[2].x = x2 + 1 - style->borderWidth.right; + points[3].x = x1 + style->borderWidth.left; + points[2].y = points[3].y = y1 + style->borderWidth.top; + view->drawPolygon (style->borderColor.top, shading, filled, convex, + points, 4); + break; + } +} + +static void drawBorderBottom(View *view, Style *style, + int x1, int y1, int x2, int y2) + +{ + int d, w; + Point points[4]; + const bool filled = true, convex = true; + bool ridge = false, inset = false, dotted = false; + Color::Shading shading = Color::SHADING_NORMAL; + + if (!style->borderColor.bottom || style->borderWidth.bottom == 0) + return; + + switch (style->borderStyle.bottom) { + case BORDER_NONE: + case BORDER_HIDDEN: + break; + case BORDER_DOTTED: + dotted = true; + case BORDER_DASHED: + w = style->borderWidth.bottom; + view->drawTypedLine(style->borderColor.bottom, shading, + dotted ? LINE_DOTTED : LINE_DASHED, + w, x1+w/2, y1-w/2, x2-w/2, y2-w/2); + break; + case BORDER_SOLID: + case BORDER_INSET: + inset = true; + case BORDER_OUTSET: + if (style->borderStyle.bottom != BORDER_SOLID) + shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK; + + if (style->borderWidth.bottom == 1) { /* 1 pixel line */ + view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2); + } else { + points[0].x = x1 - 1; + points[1].x = x2 + 2; + points[0].y = points[1].y = y1 + 1; + points[2].x = points[1].x - style->borderWidth.right; + points[3].x = points[0].x + style->borderWidth.left; + points[2].y = points[3].y = points[0].y-style->borderWidth.bottom; + view->drawPolygon (style->borderColor.bottom, shading, filled, convex, + points, 4); + } + break; + case BORDER_RIDGE: + ridge = true; + case BORDER_GROOVE: + w = style->borderWidth.bottom; + d = w & 1; + points[0].x = x1 - 1; + points[1].x = x2 + 2 - d; + points[0].y = points[1].y = y1 + 1; + points[2].x = points[1].x - style->borderWidth.right / 2; + points[3].x = points[0].x + style->borderWidth.left / 2 + d; + points[2].y = points[3].y = points[0].y - w/2 - d; + shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + view->drawPolygon (style->borderColor.bottom, shading, filled, convex, + points, 4); + // clockwise + points[0].x = x1 + style->borderWidth.left - 1; + points[1].x = x2 + 1 - style->borderWidth.right + 1; + points[0].y = points[1].y = y1 - w + 1; + points[2].x = points[1].x + style->borderWidth.right / 2; + points[3].x = points[0].x - style->borderWidth.left / 2; + points[2].y = points[3].y = points[0].y + w/2; + shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; + view->drawPolygon (style->borderColor.bottom, shading, filled, convex, + points, 4); + break; + case BORDER_DOUBLE: + w = (int) rint(style->borderWidth.bottom / 3.0); + d = w ? style->borderWidth.bottom - 2 * w : 0; + int w_l = (int) rint(style->borderWidth.left / 3.0); + int w_r = (int) rint(style->borderWidth.right / 3.0); + if (style->borderWidth.bottom == 1) { + view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2); + break; + } + points[0].x = x2 + 2; + points[1].x = x1 - 1; + points[0].y = points[1].y = y1 + 1; + points[2].x = points[1].x + w_l; + points[3].x = points[0].x - w_r; + points[2].y = points[3].y = points[0].y - w; + view->drawPolygon (style->borderColor.bottom, shading, filled, convex, + points, 4); + points[0].x = x2 + 2 - style->borderWidth.right + w_r; + points[1].x = x1 - 1 + style->borderWidth.left - w_l; + points[0].y = points[1].y = y1 + 1 - w - d; + points[2].x = x1 - 1 + style->borderWidth.left; + points[3].x = x2 + 2 - style->borderWidth.right; + points[2].y = points[3].y = y1 + 1 - style->borderWidth.bottom; + view->drawPolygon (style->borderColor.bottom, shading, filled, convex, + points, 4); + break; + } +} + +static void drawBorderLeft(View *view, Style *style, + int x1, int y1, int x2, int y2) + +{ + int d, w; + Point points[4]; + bool filled = true, convex = true; + bool ridge = false, inset = false, dotted = false; + Color::Shading shading = Color::SHADING_NORMAL; + + if (!style->borderColor.left || style->borderWidth.left == 0) + return; + + switch (style->borderStyle.left) { + case BORDER_NONE: + case BORDER_HIDDEN: + break; + case BORDER_DOTTED: + dotted = true; + case BORDER_DASHED: + w = style->borderWidth.left; + view->drawTypedLine(style->borderColor.left, shading, + dotted ? LINE_DOTTED : LINE_DASHED, + w, x1+w/2, y1+w/2, x1+w/2, y2-w/2); + break; + case BORDER_SOLID: + case BORDER_INSET: + inset = true; + case BORDER_OUTSET: + if (style->borderStyle.left != BORDER_SOLID) + shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + if (style->borderWidth.left == 1) { /* 1 pixel line */ + view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2); + } else { + points[0].x = points[1].x = x1; + points[0].y = y1 - 1; + points[1].y = y2 + 1; + points[2].x = points[3].x = points[0].x + style->borderWidth.left; + points[2].y = points[1].y - style->borderWidth.bottom; + points[3].y = points[0].y + style->borderWidth.top; + view->drawPolygon (style->borderColor.left, shading, filled, convex, + points, 4); + } + break; + case BORDER_RIDGE: + ridge = true; + case BORDER_GROOVE: + w = style->borderWidth.left; + d = w & 1; + points[0].x = points[1].x = x1; + points[0].y = y1; + points[1].y = y2; + points[2].x = points[3].x = x1 + w / 2 + d; + points[2].y = y2 - style->borderWidth.bottom / 2; + points[3].y = y1 + style->borderWidth.top / 2; + shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; + view->drawPolygon (style->borderColor.left, shading, filled, convex, + points, 4); + points[0].x = points[1].x = x1 + w / 2 + d; + points[0].y = y1 + style->borderWidth.top / 2; + points[1].y = y2 - style->borderWidth.bottom / 2; + points[2].x = points[3].x = x1 + w; + points[2].y = y2 - style->borderWidth.bottom; + points[3].y = y1 + style->borderWidth.top; + shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + view->drawPolygon (style->borderColor.left, shading, filled, convex, + points, 4); + break; + case BORDER_DOUBLE: + w = (int) rint(style->borderWidth.left / 3.0); + d = w ? style->borderWidth.left - 2 * w : 0; + int w_b = (int) rint(style->borderWidth.bottom / 3.0); + int w_t = (int) rint(style->borderWidth.top / 3.0); + if (style->borderWidth.left == 1) { + view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2-1); + break; + } + points[0].x = points[1].x = x1; + points[0].y = y1 - 1; + points[1].y = y2 + 1; + points[2].x = points[3].x = points[0].x + w; + points[2].y = points[1].y - w_b; + points[3].y = points[0].y + w_t; + view->drawPolygon (style->borderColor.left, shading, filled, convex, + points, 4); + points[0].x = points[1].x = x1 + w + d; + points[0].y = y1 - 1 + style->borderWidth.top - w_t; + points[1].y = y2 + 1 - style->borderWidth.bottom + w_b; + points[2].x = points[3].x = points[0].x + w; + points[2].y = y2 + 1 - style->borderWidth.bottom; + points[3].y = y1 - 1 + style->borderWidth.top; + view->drawPolygon (style->borderColor.left, shading, filled, convex, + points, 4); + break; + } +} + +static void drawBorderRight(View *view, Style *style, + int x1, int y1, int x2, int y2) + +{ + int d, w; + Point points[4]; + const bool filled = true, convex = true; + bool ridge = false, inset = false, dotted = false; + Color::Shading shading = Color::SHADING_NORMAL; + + if (!style->borderColor.right || style->borderWidth.right == 0) + return; + + switch (style->borderStyle.right) { + case BORDER_NONE: + case BORDER_HIDDEN: + break; + case BORDER_DOTTED: + dotted = true; + case BORDER_DASHED: + w = style->borderWidth.right; + view->drawTypedLine(style->borderColor.right, shading, + dotted ? LINE_DOTTED : LINE_DASHED, + w, x1 - w/2, y1 + w/2, x1 - w/2, y2 - w/2); + break; + case BORDER_SOLID: + case BORDER_INSET: + inset = true; + case BORDER_OUTSET: + if (style->borderStyle.right != BORDER_SOLID) + shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK; + if (style->borderWidth.right == 1) { /* 1 pixel line */ + view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2); + } else { + points[0].x = points[1].x = x1 + 1; + points[0].y = y1 - 1; + points[1].y = y2 + 1; + points[2].x = points[3].x = points[0].x-style->borderWidth.right; + points[2].y = points[1].y - style->borderWidth.bottom; + points[3].y = points[0].y + style->borderWidth.top; + view->drawPolygon (style->borderColor.right, shading, filled, convex, + points,4); + } + break; + case BORDER_RIDGE: + ridge = true; + case BORDER_GROOVE: + w = style->borderWidth.right; + d = w & 1; + points[0].x = points[1].x = x1 + 1; + points[0].y = y1; + points[1].y = y2; + points[2].x = points[3].x = points[0].x - w / 2 - d; + points[2].y = y2 - style->borderWidth.bottom / 2; + points[3].y = points[0].y + style->borderWidth.top / 2; + shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; + view->drawPolygon (style->borderColor.right, shading, filled, convex, + points, 4); + points[0].x = points[1].x = x1 + 1 - w / 2 - d; + points[0].y = y1 + style->borderWidth.top / 2; + points[1].y = y2 - style->borderWidth.bottom / 2; + points[2].x = points[3].x = x1 + 1 - w; + points[2].y = y2 - style->borderWidth.bottom; + points[3].y = y1 + style->borderWidth.top; + shading = (ridge) ? Color::SHADING_LIGHT: Color::SHADING_DARK; + view->drawPolygon (style->borderColor.right, shading, filled, convex, + points, 4); + break; + case BORDER_DOUBLE: + w = (int) rint(style->borderWidth.right / 3.0); + d = w ? style->borderWidth.right - 2 * w : 0; + int w_b = (int) rint(style->borderWidth.bottom / 3.0); + int w_t = (int) rint(style->borderWidth.top / 3.0); + if (style->borderWidth.right == 1) { + view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2); + break; + } + points[0].x = points[1].x = x1 + 1; + points[0].y = y1 - 1; + points[1].y = y2 + 1; + points[2].x = points[3].x = points[0].x - w; + points[2].y = points[1].y - w_b; + points[3].y = points[0].y + w_t; + view->drawPolygon (style->borderColor.right, shading, filled, convex, + points, 4); + points[0].x = points[1].x = x1 + 1 - w - d; + points[0].y = y1 - 1 + style->borderWidth.top - w_t; + points[1].y = y2 + 1 - style->borderWidth.bottom + w_b; + points[2].x = points[3].x = points[0].x - w; + points[2].y = y2 + 1 - style->borderWidth.bottom; + points[3].y = y1 - 1 + style->borderWidth.top; + view->drawPolygon (style->borderColor.right, shading, filled, convex, + points, 4); + break; + } +} + +/** + * \brief Draw the border of a region in window, according to style. + * + * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + * + * "area" is the area to be drawn, "x", "y", "width" and "height" + * define the box itself. All are given in canvas coordinates. + */ +void drawBorder (View *view, Layout *layout, Rectangle *area, + int x, int y, int width, int height, + Style *style, bool inverse) +{ + /** \todo a lot! */ + int xb1, yb1, xb2, yb2; + + // top left and bottom right point of outer border boundary + xb1 = x + style->margin.left; + yb1 = y + style->margin.top; + xb2 = x + (width > 0 ? width - 1 : 0) - style->margin.right; + yb2 = y + (height > 0 ? height - 1 : 0) - style->margin.bottom; + + /* + // top left and bottom right point of inner border boundary + xp1 = xb1 + style->borderWidth.left; + yp1 = yb1 + style->borderWidth.top; + xp2 = xb2 - style->borderWidth.right; + yp2 = yb2 - style->borderWidth.bottom; + + light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT; + dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK; + normal = inverse ? Color::SHADING_INVERSE : Color::SHADING_NORMAL; + */ + + drawBorderRight(view, style, xb2, yb1, xb2, yb2); + drawBorderLeft(view, style, xb1, yb1, xb1, yb2); + drawBorderTop(view, style, xb1, yb1, xb2, yb1); + drawBorderBottom(view, style, xb1, yb2, xb2, yb2); +} + + +/** + * \brief Draw the background (content plus padding) of a region in window, + * according to style. + * + * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + * + * "area" is the area to be drawn, "x", "y", "width" and "height" + * define the box itself (padding box). "xRef", "yRef", "widthRef" and + * "heightRef" define the reference area, which is important for the + * tiling of background images (for position 0%/0%, a tile is set at + * xRef/yRef; for position 100%/100%, a tile is set at xRef + + * widthRef/yRef + widthRef). See calls for more informations; in most + * cases, these boxes are identical (padding box). All these + * coordinates are given in canvas coordinates. + * + * "atTop" should be true, only if the area is drawn directly on the + * canvas, not on top of other areas; this is only true for the + * toplevel widget itself (not parts of its contents). Toplevel widget + * background colors are already set as viewport background color, so + * that drawing again is is not neccessary, but some time can be + * saved. + * + * Otherwise, the caller should not try to increase the performance by + * doing some tests before; this is all done in this method. + * + * "bgColor" is passes implicitly. For non-inversed drawing, + * style->backgroundColor may simply used. However, when drawing is + * inversed, and style->backgroundColor is undefined (NULL), a + * background color defined higher in the hierarchy (which is not + * accessable here) must be used. + * + * (Background *images* are never drawn inverse.) + */ +void drawBackground (View *view, Layout *layout, Rectangle *area, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef, + Style *style, Color *bgColor, bool inverse, bool atTop) +{ + bool hasBgColor = bgColor != NULL && + // The test for background colors is rather simple, since only the color + // has to be compared, ... + (!atTop || layout->getBgColor () != bgColor); + bool hasBgImage = (style->backgroundImage != NULL && + style->backgroundImage->getImgbufSrc() != NULL) && + // ... but for backgrounds, it would be rather complicated. To handle the + // two cases (normal HTML in a viewport, where the layout background + // image is set, and contents of <button> within a flat view, where the + // background image of the toplevel widget is set), only the background + // images are compared. A full test, which also deals with all other + // attributes related to background images (repeat, position etc.) would + // be complicated and useless, so not worth the work. + (!atTop || layout->getBgImage () != style->backgroundImage); + + // Since widgets are always drawn from top to bottom, it is *not* + // necessary to draw the background if background color and image + // are not set (NULL), i. e. shining through. + + if (hasBgColor || hasBgImage) { + Rectangle bgArea, intersection; + bgArea.x = x; + bgArea.y = y; + bgArea.width = width; + bgArea.height = height; + + if (area->intersectsWith (&bgArea, &intersection)) { + if (hasBgColor) + view->drawRectangle (bgColor, + inverse ? + Color::SHADING_INVERSE : Color::SHADING_NORMAL, + true, intersection.x, intersection.y, + intersection.width, intersection.height); + + if (hasBgImage) + drawBackgroundImage (view, style->backgroundImage, + style->backgroundRepeat, + style->backgroundAttachment, + style->backgroundPositionX, + style->backgroundPositionY, + intersection.x, intersection.y, + intersection.width, intersection.height, + xRef, yRef, widthRef, heightRef); + + } + } +} + +void drawBackgroundImage (View *view, StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef) +{ + //printf ("drawBackgroundImage (..., [img: %d, %d], ..., (%d, %d), %d x %d, " + // "(%d, %d), %d x %d)\n", imgWidth, imgHeight, x, y, width, height, + // xRef, yRef, widthRef, heightRef); + + bool repeatX, repeatY, doDraw; + int origX, origY, tileX1, tileX2, tileY1, tileY2; + + calcBackgroundRelatedValues (backgroundImage, backgroundRepeat, + backgroundAttachment, backgroundPositionX, + backgroundPositionY, x, y, width, height, + xRef, yRef, widthRef, heightRef, + &repeatX, &repeatY, &origX, &origY, + &tileX1, &tileX2, &tileY1, &tileY2, &doDraw); + + //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n", + // tileX1, tileX2, tileY1, tileY2); + + if (doDraw) { + // Drawing is done with the "tiled" buffer, but all calculations + // before have been done with the "source" buffer. + + Imgbuf *imgbufS = backgroundImage->getImgbufSrc(); + int imgWidthS = imgbufS->getRootWidth (); + int imgHeightS = imgbufS->getRootHeight (); + + Imgbuf *imgbufT = backgroundImage->getImgbufTiled(repeatX, repeatY); + int imgWidthT = imgbufT->getRootWidth (); + int imgHeightT = imgbufT->getRootHeight (); + int tilesX = backgroundImage->getTilesX (repeatX, repeatY); + int tilesY = backgroundImage->getTilesY (repeatX, repeatY); + + for (int tileX = tileX1; tileX <= tileX2; tileX += tilesX) + for (int tileY = tileY1; tileY <= tileY2; tileY += tilesY) { + int xt = origX + tileX * imgWidthS; + int x1 = misc::max (xt, x); + int x2 = misc::min (xt + imgWidthT, x + width); + int yt = origY + tileY * imgHeightS; + int y1 = misc::max (yt, y); + int y2 = misc::min (yt + imgHeightT, y + height); + + view->drawImage (imgbufT, xt, yt, x1 - xt, y1 - yt, + x2 - x1, y2 - y1); + } + } +} + +void calcBackgroundRelatedValues (StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int xDraw, int yDraw, int widthDraw, + int heightDraw, int xRef, int yRef, + int widthRef, int heightRef, bool *repeatX, + bool *repeatY, int *origX, int *origY, + int *tileX1, int *tileX2, int *tileY1, + int *tileY2, bool *doDraw) +{ + Imgbuf *imgbuf = backgroundImage->getImgbufSrc(); + int imgWidth = imgbuf->getRootWidth (); + int imgHeight = imgbuf->getRootHeight (); + + *repeatX = backgroundRepeat == BACKGROUND_REPEAT || + backgroundRepeat == BACKGROUND_REPEAT_X; + *repeatY = backgroundRepeat == BACKGROUND_REPEAT || + backgroundRepeat == BACKGROUND_REPEAT_Y; + + *origX = xRef + + (isPerLength (backgroundPositionX) ? + multiplyWithPerLength (widthRef - imgWidth, backgroundPositionX) : + absLengthVal (backgroundPositionX)); + *origY = yRef + + (isPerLength (backgroundPositionY) ? + multiplyWithPerLength (heightRef - imgHeight, backgroundPositionY) : + absLengthVal (backgroundPositionY)); + + *tileX1 = xDraw < *origX ? + - (*origX - xDraw + imgWidth - 1) / imgWidth : + (xDraw - *origX) / imgWidth; + *tileX2 = *origX < xDraw + widthDraw ? + (xDraw + widthDraw - *origX - 1) / imgWidth : + - (*origX - (xDraw + widthDraw) + imgWidth - 1) / imgWidth; + *tileY1 = yDraw < *origY ? + - (*origY - yDraw + imgHeight - 1) / imgHeight : + (yDraw - *origY) / imgHeight; + *tileY2 = *origY < yDraw + heightDraw ? + (yDraw + heightDraw - *origY - 1) / imgHeight : + - (*origY - (yDraw + heightDraw) + imgHeight - 1) / imgHeight; + + *doDraw = true; + if (!*repeatX) { + // Only center tile (tileX = 0) is drawn, ... + if (*tileX1 <= 0 && *tileX2 >= 0) + // ... and is visible. + *tileX1 = *tileX2 = 0; + else + // ... but is not visible. + *doDraw = false; + } + + if (!*repeatY) { + // Analogue. + if (*tileY1 <= 0 && *tileY2 >= 0) + *tileY1 = *tileY2 = 0; + else + *doDraw = false; + } +} + +// ---------------------------------------------------------------------- + +static const char + *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, + *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, + *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, + *const roman_I3[] = { "","M","MM","MMM","MMMM" }; + +static void strAsciiTolower (char *s) +{ + for ( ; *s; s++) + *s = misc::AsciiTolower (*s); +} + +/** + * \brief Convert a number into a string, in a given list style. + * + * Used for ordered lists. + */ +void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType) +{ + int i3, i2, i1, i0; + bool low = false; + int start_ch = 'A'; + + if (buflen <= 0) + return; + + switch(listStyleType){ + case LIST_STYLE_TYPE_LOWER_ALPHA: + case LIST_STYLE_TYPE_LOWER_LATIN: + start_ch = 'a'; + case LIST_STYLE_TYPE_UPPER_ALPHA: + case LIST_STYLE_TYPE_UPPER_LATIN: + i0 = num - 1; + i1 = i0/26 - 1; i2 = i1/26 - 1; + if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */ + snprintf(buf, buflen, "****."); + else + snprintf(buf, buflen, "%c%c%c.", + i2<0 ? ' ' : start_ch + i2%26, + i1<0 ? ' ' : start_ch + i1%26, + i0<0 ? ' ' : start_ch + i0%26); + break; + case LIST_STYLE_TYPE_LOWER_ROMAN: + low = true; + case LIST_STYLE_TYPE_UPPER_ROMAN: + i0 = num; + i1 = i0/10; i2 = i1/10; i3 = i2/10; + i0 %= 10; i1 %= 10; i2 %= 10; + if (num < 0 || i3 > 4) /* more than 4999 elements ? */ + snprintf(buf, buflen, "****."); + else + snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2], + roman_I1[i1], roman_I0[i0]); + break; + case LIST_STYLE_TYPE_DECIMAL: + default: + snprintf(buf, buflen, "%d.", num); + break; + } + + // ensure termination + buf[buflen - 1] = '\0'; + + if (low) + strAsciiTolower(buf); + +} + +} // namespace style +} // namespace core +} // namespace dw diff --git a/dw/style.hh b/dw/style.hh new file mode 100644 index 0000000..230baa2 --- /dev/null +++ b/dw/style.hh @@ -0,0 +1,907 @@ +#ifndef __DW_STYLE_HH__ +#define __DW_STYLE_HH__ + +#include <stdint.h> + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/signal.hh" +#include "../lout/debug.hh" + +namespace dw { +namespace core { + +/** + * \brief Anything related to Dillo %Widget styles is defined here. + * + * <h3>Overview</h3> + * + * dw::core::style::Style provides some resources and attributes for + * drawing widgets, as well as for parts of a widget (e.g., dw::Textblock + * uses styles for its words). Creating a style is done by filling a + * dw::core::style::StyleAttrs with the attributes and calling + * dw::core::style::Style::create: + * + * \code + * dw::core::style::Style styleAttrs; + * dw::core::style::Style *style; + * dw::core::Layout *layout; + * + * // ... + * + * styleAttrs.foo = bar; + * // etc. + * style = dw::core::style::Style::create (&styleAttrs, layout); + * // do something with style + * \endcode + * + * After this, the attributes of a dw::core::style::Style should not be + * changed anymore, since styles are often shared between different + * widgets etc. (see below). Most times, you simply copy the attributes + * of another style (possible, since dw::core::style::Style is a sub + * class of dw::core::style::StyleAttrs), modify them and create a new + * style: + * + * \code + * styleAttrs = *anotherStyle; + * styleAttrs.foo = baz; + * style = dw::core::style::Style::create (&styleAttrs, layout); + * \endcode + * + * The dw::core::style::Font structure can be created by + * dw::core::style::Font::create, in a similar, with + * dw::core::style::FontAttrs, and colors by + * dw::core::style::Color::create, passing 0xrrggbb as an + * argument. Furthermore, there is dw::core::style::Tooltip, created by + * dw::core::style::Tooltip::create. + * + * Notice that fonts, colors and tooltips are only intended to be used in + * conjunction with dw::core::style::Style. + * + * + * <h3>Naming</h3> + * + * dw::core::style::Style will become important for CSS, each CSS + * attribute, which is supported by dillo, will refer to an attribute in + * dw::core::style::Style. For this reason, the attributes in + * dw::core::style::Style get the names from the CSS attributes, with + * "camelCase" instead of hyphens (e.g. "background-color" becomes + * "backgroundColor"). + * + * However, dw::core::style::Style will be extended by some more + * attributes, which are not defined by CSS. To distinguish them, they + * get the prefix "x_", e.g. dw::core::style::Style::x_link. + * + * + * <h3>Lengths and Percentages</h3> + * + * dw::core::style::Length is a simple data type for lengths and + * percentages: + * + * <ul> + * <li> A length refers to an absolute measurement. It is used to + * represent the HTML type %Pixels; and the CSS type \<length\>. + * + * For CSS lengths, there are two units: (i) pixels and absolute + * units, which have to be converted to pixels (a pixel is, unlike + * in the CSS specification, treated as absolute unit), and (ii) the + * relative units "em" and "ex" (see below). + * + * <li> A percentage refers to a value relative to another value. It is + * used for the HTML type %Length; (except %Pixels;), and the CSS + * type \<percentage\>. + * + * <li> A relative length can be used in lists of HTML MultiLengths. + * </ul> + * + * Since many values in CSS may be either lengths or percentages, a + * single type is very useful. + * + * <h4>Useful Functions</h4> + * + * Creating lengths: + * + * <ul> + * <li> dw::core::style::createAbsLength + * <li> dw::core::style::createPerLength + * <li> dw::core::style::createRelLength + * </ul> + * + * Examine lengths: + * + * <ul> + * <li> dw::core::style::isAbsLength + * <li> dw::core::style::isPerLength + * <li> dw::core::style::isRelLength + * <li> dw::core::style::absLengthVal + * <li> dw::core::style::perLengthVal + * <li> dw::core::style::relLengthVal + * </ul> + * + * + * <h3>Boxes</h3> + * + * <h4>The CSS %Box Model</h4> + * + * For borders, margins etc., the box model defined by CSS2 is + * used. dw::core::style::Style contains some members defining these + * attributes. A dw::core::Widget must use these values for any + * calculation of sizes. There are some helper functions (see + * dw/style.hh). A dw::core::style::Style box looks quite similar to a + * CSS box: + * + * \image html dw-style-box-model.png + * + * <h4>Background colors</h4> + * + * The background color is stored in + * dw::core::style::Style::backgroundColor, which may be NULL (the + * background color of the parent widget is shining through). + * + * For toplevel widgets, this color is set as the background color of the + * views (dw::core::View::setBgColor), for other widgets, a filled + * rectangle is drawn, covering the content and padding. (This is + * compliant with CSS2, the background color of the toplevel element + * covers the whole canvas.) + * + * <h4>Drawing</h4> + * + * The following methods may be useful: + * + * <ul> + * <li> dw::core::Widget::drawWidgetBox for drawing the box of a widget + * (typically at the beginning of the implementation of + * dw::core::Widget::draw), and + * + * <li> dw::core::Widget::drawBox, for drawing parts of a widget (e.g. + * dw::Textblock::Word, which has its own dw::Textblock::Word::style). + * </ul> + * + * + * <h3>Notes on Memory Management</h3> + * + * Memory management is done by reference counting, + * dw::core::style::Style::create returns a pointer to + * dw::core::style::Style with an increased reference counter, so you + * should care about calling dw::core::style::Style::unref if it is not + * used anymore. You do \em not need to care about the reference counters + * of fonts and styles. + * + * In detail: + * + * <ul> + * <li> dw::core::style::Style::ref is called in + * + * <ul> + * <li> dw::core::Widget::setStyle to assign a style to a widget, + * <li> dw::Textblock::addText, dw::Textblock::addWidget, + * dw::Textblock::addAnchor, dw::Textblock::addSpace, + * dw::Textblock::addParbreak and dw::Textblock::addLinebreak, + * to assign a style to a dw::Textblock::Word, and + * <li> by the HTML parser, when pushing an element on the stack. + * </ul> + * + * <li> dw::core::style::Style::unref is called in + * + * <ul> + * <li> dw::core::Widget::~Widget, dw::Textblock::~Textblock, by the + * HTML parser, when popping an element fom the stack, and + * <li> dw::core::Widget::setStyle, dw::Textblock::addText etc., + * these methods overwrite an existing style. + * </ul> + * </ul> + */ +namespace style { + +enum Cursor { + CURSOR_CROSSHAIR, + CURSOR_DEFAULT, + CURSOR_POINTER, + CURSOR_MOVE, + CURSOR_E_RESIZE, + CURSOR_NE_RESIZE, + CURSOR_NW_RESIZE, + CURSOR_N_RESIZE, + CURSOR_SE_RESIZE, + CURSOR_SW_RESIZE, + CURSOR_S_RESIZE, + CURSOR_W_RESIZE, + CURSOR_TEXT, + CURSOR_WAIT, + CURSOR_HELP +}; + +enum BorderCollapse { + BORDER_MODEL_SEPARATE, + BORDER_MODEL_COLLAPSE +}; + +enum BorderStyle { + BORDER_NONE, + BORDER_HIDDEN, + BORDER_DOTTED, + BORDER_DASHED, + BORDER_SOLID, + BORDER_DOUBLE, + BORDER_GROOVE, + BORDER_RIDGE, + BORDER_INSET, + BORDER_OUTSET +}; + +enum BackgroundRepeat { + BACKGROUND_REPEAT, + BACKGROUND_REPEAT_X, + BACKGROUND_REPEAT_Y, + BACKGROUND_NO_REPEAT +}; + +enum BackgroundAttachment { + BACKGROUND_ATTACHMENT_SCROLL, + BACKGROUND_ATTACHMENT_FIXED +}; + +enum TextAlignType { + TEXT_ALIGN_LEFT, + TEXT_ALIGN_RIGHT, + TEXT_ALIGN_CENTER, + TEXT_ALIGN_JUSTIFY, + TEXT_ALIGN_STRING +}; + +enum VAlignType { + VALIGN_TOP, + VALIGN_BOTTOM, + VALIGN_MIDDLE, + VALIGN_BASELINE, + VALIGN_SUB, + VALIGN_SUPER, + VALIGN_TEXT_TOP, + VALIGN_TEXT_BOTTOM, +}; + +enum TextTransform { + TEXT_TRANSFORM_NONE, + TEXT_TRANSFORM_CAPITALIZE, + TEXT_TRANSFORM_UPPERCASE, + TEXT_TRANSFORM_LOWERCASE, +}; + +/** + * \todo Incomplete. Has to be completed for a CSS implementation. + */ +enum DisplayType { + DISPLAY_BLOCK, + DISPLAY_INLINE, + DISPLAY_INLINE_BLOCK, + DISPLAY_LIST_ITEM, + DISPLAY_NONE, + DISPLAY_TABLE, + DISPLAY_TABLE_ROW_GROUP, + DISPLAY_TABLE_HEADER_GROUP, + DISPLAY_TABLE_FOOTER_GROUP, + DISPLAY_TABLE_ROW, + DISPLAY_TABLE_CELL +}; + +enum LineType { + LINE_NORMAL, + LINE_DOTTED, + LINE_DASHED +}; + +enum ListStylePosition { + LIST_STYLE_POSITION_INSIDE, + LIST_STYLE_POSITION_OUTSIDE +}; +enum ListStyleType { + LIST_STYLE_TYPE_DISC, + LIST_STYLE_TYPE_CIRCLE, + LIST_STYLE_TYPE_SQUARE, + LIST_STYLE_TYPE_DECIMAL, + LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO, + LIST_STYLE_TYPE_LOWER_ROMAN, + LIST_STYLE_TYPE_UPPER_ROMAN, + LIST_STYLE_TYPE_LOWER_GREEK, + LIST_STYLE_TYPE_LOWER_ALPHA, + LIST_STYLE_TYPE_LOWER_LATIN, + LIST_STYLE_TYPE_UPPER_ALPHA, + LIST_STYLE_TYPE_UPPER_LATIN, + LIST_STYLE_TYPE_HEBREW, + LIST_STYLE_TYPE_ARMENIAN, + LIST_STYLE_TYPE_GEORGIAN, + LIST_STYLE_TYPE_CJK_IDEOGRAPHIC, + LIST_STYLE_TYPE_HIRAGANA, + LIST_STYLE_TYPE_KATAKANA, + LIST_STYLE_TYPE_HIRAGANA_IROHA, + LIST_STYLE_TYPE_KATAKANA_IROHA, + LIST_STYLE_TYPE_NONE +}; + +enum FontStyle { + FONT_STYLE_NORMAL, + FONT_STYLE_ITALIC, + FONT_STYLE_OBLIQUE +}; + +enum FontVariant { + FONT_VARIANT_NORMAL, + FONT_VARIANT_SMALL_CAPS +}; + +enum Overflow { + OVERFLOW_VISIBLE, + OVERFLOW_HIDDEN, + OVERFLOW_SCROLL, + OVERFLOW_AUTO +}; + +enum Position { + POSITION_STATIC, + POSITION_RELATIVE, + POSITION_ABSOLUTE, + POSITION_FIXED, +}; + +enum TextDecoration { + TEXT_DECORATION_NONE = 0, + TEXT_DECORATION_UNDERLINE = 1 << 0, + TEXT_DECORATION_OVERLINE = 1 << 1, + TEXT_DECORATION_LINE_THROUGH = 1 << 2, + TEXT_DECORATION_BLINK = 1 << 3 +}; + +enum WhiteSpace { + WHITE_SPACE_NORMAL, + WHITE_SPACE_PRE, + WHITE_SPACE_NOWRAP, + WHITE_SPACE_PRE_WRAP, + WHITE_SPACE_PRE_LINE, +}; + +enum FloatType { + FLOAT_NONE, + FLOAT_LEFT, + FLOAT_RIGHT +}; + +enum ClearType { + CLEAR_LEFT, + CLEAR_RIGHT, + CLEAR_BOTH, + CLEAR_NONE +}; + +/** + * \brief Type for representing all lengths within dw::core::style. + * + * Lengths are int's. Absolute lengths are represented in the following way: + * + * \image html dw-style-length-absolute.png + * + * Percentages: + * + * \image html dw-style-length-percentage.png + * + * Relative lengths (only used in HTML): + * + * \image html dw-style-length-relative.png + * + * This is an implementation detail, use one of the following functions: + * + * Creating lengths: + * + * <ul> + * <li> dw::core::style::createAbsLength + * <li> dw::core::style::createPerLength + * <li> dw::core::style::createRelLength + * </ul> + * + * Examine lengths: + * + * <ul> + * <li> dw::core::style::isAbsLength + * <li> dw::core::style::isPerLength + * <li> dw::core::style::isRelLength + * <li> dw::core::style::absLengthVal + * <li> dw::core::style::perLengthVal + * <li> dw::core::style::relLengthVal + * </ul> + * + * "auto" lengths are represented as dw::core::style::LENGTH_AUTO. + */ +typedef int Length; + +/** \brief Returns a length of \em n pixels. */ +inline Length createAbsLength(int n) { return (n << 2) | 1; } + +/** \brief Returns a percentage, \em v is relative to 1, not to 100. */ +inline Length createPerLength(double v) { + return ((int)(v * (1 << 18)) & ~3) | 2; } + +/** \brief Returns a relative length. */ +inline Length createRelLength(double v) { + return ((int)(v * (1 << 18)) & ~3) | 3; } + +/** \brief Returns true if \em l is an absolute length. */ +inline bool isAbsLength(Length l) { return (l & 3) == 1; } + +/** \brief Returns true if \em l is a percentage. */ +inline bool isPerLength(Length l) { return (l & 3) == 2; } + +/** \brief Returns true if \em l is a relative length. */ +inline bool isRelLength(Length l) { return (l & 3) == 3; } + +/** \brief Returns the value of a length in pixels, as an integer. */ +inline int absLengthVal(Length l) { return l >> 2; } + +/** \brief Returns the value of a percentage, relative to 1, as a double. + * + * When possible, do not use this function directly; it may be removed + * soon. Instead, use multiplyWithPerLength or multiplyWithPerLengthRounded. + */ +inline double perLengthVal_useThisOnlyForDebugging(Length l) +{ return (double)(l & ~3) / (1 << 18); } + +/** \brief Returns the value of a relative length, as a float. + * + * When possible, do not use this function directly; it may be removed + * soon. + */ +inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); } + +/** + * \brief Multiply an int with a percentage length, returning int. + * + * Use this instead of perLengthVal, when possible. + */ +inline int multiplyWithPerLength(int x, Length l) { + return x * perLengthVal_useThisOnlyForDebugging (l); +} + +/** + * \brief Like multiplyWithPerLength, but rounds to nearest integer + * instead of down. + * + * (This function exists for backward compatibility.) + */ +inline int multiplyWithPerLengthRounded(int x, Length l) { + return lout::misc::roundInt (x * perLengthVal_useThisOnlyForDebugging (l)); +} + +inline int multiplyWithRelLength(int x, Length l) { + return x * relLengthVal(l); +} + + +enum { + /** \brief Represents "auto" lengths. */ + LENGTH_AUTO = 0 +}; + +/** + * \brief Represents a dimension box according to the CSS box model. + * + * Used for dw::core::style::Style::margin, + * dw::core::style::Style::borderWidth, and dw::core::style::Style::padding. + */ +class Box +{ +public: + /* in future also percentages */ + int top, right, bottom, left; + + inline void setVal(int val) { top = right = bottom = left = val; } + inline bool equals (Box *other) { + return top == other->top && + right == other->right && + bottom == other->bottom && + left == other->left; + } + inline int hashValue () { + return top + right + bottom + left; + } +}; + +class Tooltip; +class Font; +class Color; +class StyleImage; + +/** + * \sa dw::core::style + */ +class StyleAttrs : public lout::object::Object +{ +public: + Font *font; + int textDecoration; /* No TextDecoration because of problems converting + * TextDecoration <-> int */ + Color *color, *backgroundColor; + StyleImage *backgroundImage; + BackgroundRepeat backgroundRepeat; + BackgroundAttachment backgroundAttachment; + Length backgroundPositionX; // "left" defined by "0%" etc. (see CSS spec) + Length backgroundPositionY; // "top" defined by "0%" etc. (see CSS spec) + + TextAlignType textAlign; + VAlignType valign; + char textAlignChar; /* In future, strings will be supported. */ + TextTransform textTransform; + + FloatType vloat; /* "float" is a keyword. */ + ClearType clear; + + Overflow overflow; + + Position position; + Length top, bottom, left, right; + + int hBorderSpacing, vBorderSpacing, wordSpacing; + Length width, height, lineHeight, textIndent; + Length minWidth, maxWidth, minHeight, maxHeight; + + Box margin, borderWidth, padding; + BorderCollapse borderCollapse; + struct { Color *top, *right, *bottom, *left; } borderColor; + struct { BorderStyle top, right, bottom, left; } borderStyle; + + DisplayType display; + WhiteSpace whiteSpace; + ListStylePosition listStylePosition; + ListStyleType listStyleType; + Cursor cursor; + + int x_link; + int x_img; + Tooltip *x_tooltip; + char x_lang[2]; /* Either x_lang[0] == x_lang[1] == 0 (no language + set), or x_lang contains the RFC 1766 country + code in lower case letters. (Only two letters + allowed, currently.) */ + + void initValues (); + void resetValues (); + + bool sizeDiffs (StyleAttrs *otherStyleAttrs); + + inline void setBorderColor(Color *val) { + borderColor.top = borderColor.right = borderColor.bottom + = borderColor.left = val; } + inline void setBorderStyle(BorderStyle val) { + borderStyle.top = borderStyle.right = borderStyle.bottom + = borderStyle.left = val; } + + inline int boxOffsetX () + { return margin.left + borderWidth.left + padding.left; } + inline int boxRestWidth () + { return margin.right + borderWidth.right + padding.right; } + inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } + inline int boxOffsetY () + { return margin.top + borderWidth.top + padding.top; } + inline int boxRestHeight () + { return margin.bottom + borderWidth.bottom + padding.bottom; } + inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + + inline bool hasBackground () + { return backgroundColor != NULL || backgroundImage != NULL; } + + bool equals (lout::object::Object *other); + int hashValue (); +}; + + +/** + * \sa dw::core::style + */ +class Style: public StyleAttrs +{ +private: + static int totalRef; + int refCount; + static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable; + + Style (StyleAttrs *attrs); + +protected: + ~Style(); + + void copyAttrs (StyleAttrs *attrs); + +public: + inline static Style *create (StyleAttrs *attrs) + { + Style *style = styleTable->get (attrs); + if (style) { + style->ref (); + } else { + style = new Style (attrs); + styleTable->put(style, style); + } + return style; + } + + inline void ref () { refCount++; } + inline void unref () { if (--refCount == 0) delete this; } +}; + + +/** + * \sa dw::core::style + */ +class TooltipAttrs: public lout::object::String +{ +public: + TooltipAttrs(const char *text): lout::object::String(text) { } +}; + +/** + * \sa dw::core::style + */ +class Tooltip: public TooltipAttrs +{ +private: + int refCount; + +protected: + Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; } + +public: + static Tooltip *create (dw::core::Layout *layout, const char *text); + inline void ref () { refCount++; } + inline void unref () + { if (--refCount == 0) delete this; } + + inline virtual void onEnter () { } + inline virtual void onLeave () { } + inline virtual void onMotion () { } +}; + + +/** + * \sa dw::core::style + */ +class FontAttrs: public lout::object::Object +{ +public: + const char *name; + int size; + int weight; + int letterSpacing; + FontVariant fontVariant; + FontStyle style; + + bool equals(lout::object::Object *other); + int hashValue(); +}; + + +/** + * \sa dw::core::style + */ +class Font: public FontAttrs +{ +private: + int refCount; + + static Font *create0 (Layout *layout, FontAttrs *attrs, bool tryEverything); + +protected: + inline Font () { + DBG_OBJ_CREATE ("dw::core::style::Font"); + refCount = 0; + } + virtual ~Font (); + + void copyAttrs (FontAttrs *attrs); + +public: + int ascent, descent; + int spaceWidth; + int xHeight; + + static Font *create (Layout *layout, FontAttrs *attrs); + static bool exists (Layout *layout, const char *name); + + inline void ref () { refCount++; } + inline void unref () { if (--refCount == 0) delete this; } +}; + + +/** + * \sa dw::core::style + */ +class ColorAttrs: public lout::object::Object +{ +protected: + int color; + +public: + inline ColorAttrs(int color) + { + this->color = color; + } + + inline int getColor () { return color; } + + bool equals(lout::object::Object *other); + int hashValue(); +}; + + +/** + * \sa dw::core::style + */ +class Color: public ColorAttrs +{ +private: + int refCount; + + void remove(dw::core::Layout *layout); + int shadeColor (int color, int d); + +protected: + inline Color (int color): ColorAttrs (color) { + DBG_OBJ_CREATE ("dw::core::style::Color"); + refCount = 0; + } + virtual ~Color (); + +public: + enum Shading { SHADING_NORMAL, SHADING_INVERSE, SHADING_DARK, SHADING_LIGHT, + SHADING_NUM }; + +protected: + int shadeColor (int color, Shading shading); + +public: + static Color *create (Layout *layout, int color); + + inline void ref () { refCount++; } + inline void unref () + { if (--refCount == 0) delete this; } +}; + + +class StyleImage: public lout::signal::ObservedObject +{ +private: + class StyleImgRenderer: public ImgRenderer + { + private: + StyleImage *image; + + public: + inline StyleImgRenderer (StyleImage *image) { this->image = image; } + + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + }; + + int refCount, tilesX, tilesY; + Imgbuf *imgbufSrc, *imgbufTiled; + ImgRendererDist *imgRendererDist; + StyleImgRenderer *styleImgRenderer; + + StyleImage (); + ~StyleImage (); + +public: + /** + * \brief Useful (but not mandatory) base class for updates of + * areas with background images. + */ + class ExternalImgRenderer: public ImgRenderer + { + public: + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + + /** + * \brief If this method returns false, nothing is done at all. + */ + virtual bool readyToDraw () = 0; + + /** + * \brief Return the area covered by the background image. + */ + virtual void getBgArea (int *x, int *y, int *width, int *height) = 0; + + /** + * \brief Return the "reference area". + * + * See comment of "drawBackground". + */ + virtual void getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) = 0; + + virtual StyleImage *getBackgroundImage () = 0; + virtual BackgroundRepeat getBackgroundRepeat () = 0; + virtual BackgroundAttachment getBackgroundAttachment () = 0; + virtual Length getBackgroundPositionX () = 0; + virtual Length getBackgroundPositionY () = 0; + + /** + * \brief Draw (or queue for drawing) an area, which is given in + * canvas coordinates. + */ + virtual void draw (int x, int y, int width, int height) = 0; + }; + + /** + * \brief Suitable for widgets and parts of widgets. + */ + class ExternalWidgetImgRenderer: public ExternalImgRenderer + { + public: + void getPaddingArea (int *x, int *y, int *width, int *height); + + StyleImage *getBackgroundImage (); + BackgroundRepeat getBackgroundRepeat (); + BackgroundAttachment getBackgroundAttachment (); + Length getBackgroundPositionX (); + Length getBackgroundPositionY (); + + /** + * \brief Return the style this background image is part of. + */ + virtual Style *getStyle () = 0; + }; + + static StyleImage *create () { return new StyleImage (); } + + inline void ref () { refCount++; } + inline void unref () + { if (--refCount == 0) delete this; } + + inline Imgbuf *getImgbufSrc () { return imgbufSrc; } + inline Imgbuf *getImgbufTiled (bool repeatX, bool repeatY) + { return (imgbufTiled && repeatX && repeatY) ? imgbufTiled : imgbufSrc; } + inline int getTilesX (bool repeatX, bool repeatY) + { return (imgbufTiled && repeatX && repeatY) ? tilesX : 1; } + inline int getTilesY (bool repeatX, bool repeatY) + { return (imgbufTiled && repeatX && repeatY) ? tilesY : 1; } + inline ImgRenderer *getMainImgRenderer () { return imgRendererDist; } + + /** + * \brief Add an additional ImgRenderer, especially used for + * drawing. + */ + inline void putExternalImgRenderer (ImgRenderer *ir) + { imgRendererDist->put (ir); } + + /** + * \brief Remove a previously added additional ImgRenderer. + */ + inline void removeExternalImgRenderer (ImgRenderer *ir) + { imgRendererDist->remove (ir); } +}; + +void drawBorder (View *view, Layout *layout, Rectangle *area, + int x, int y, int width, int height, + Style *style, bool inverse); +void drawBackground (View *view, Layout *layout, Rectangle *area, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef, + Style *style, Color *bgColor, bool inverse, bool atTop); +void drawBackgroundImage (View *view, StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef); +void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType); + +} // namespace style +} // namespace core +} // namespace dw + +#endif // __DW_STYLE_HH__ + diff --git a/dw/types.cc b/dw/types.cc new file mode 100644 index 0000000..3962d97 --- /dev/null +++ b/dw/types.cc @@ -0,0 +1,367 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include "../lout/msg.h" + +using namespace lout; + +namespace dw { +namespace core { + +Rectangle::Rectangle (int x, int y, int width, int height) +{ + this->x = x; + this->y = y; + this->width = width; + this->height = height; +} + +/* + * Draw rectangle in view relative to point (x,y). + */ +void Rectangle::draw (core::View *view, core::style::Style *style, int x,int y) +{ + const bool filled = false; + + view->drawRectangle(style->color, core::style::Color::SHADING_NORMAL,filled, + x + this->x, y + this->y, this->width, this->height); +} + +/** + * Return whether this rectangle and otherRect intersect. If yes, + * return the intersection rectangle in dest. + */ +bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest) +{ + bool doIntersect = + this->x < otherRect->x + otherRect->width && + this->y < otherRect->y + otherRect->height && + otherRect->x < this->x + this->width && + otherRect->y < this->y + this->height; + + if (doIntersect) { + dest->x = misc::max(this->x, otherRect->x); + dest->y = misc::max(this->y, otherRect->y); + dest->width = misc::min(this->x + this->width, + otherRect->x + otherRect->width) - dest->x; + dest->height = misc::min(this->y + this->height, + otherRect->y + otherRect->height) - dest->y; + } else { + dest->x = dest->y = dest->width = dest->height = 0; + } + + return doIntersect; +} + +/* + * Return whether this is a subset of otherRect. + */ +bool Rectangle::isSubsetOf (Rectangle *otherRect) +{ + return + x >= otherRect->x && + y >= otherRect->y && + x + width <= otherRect->x + otherRect->width && + y + height <= otherRect->y + otherRect->height; +} + +bool Rectangle::isPointWithin (int x, int y) +{ + return + x >= this->x && y >= this->y && + x < this->x + width && y < this->y + height; +} + +// ---------------------------------------------------------------------- + +Circle::Circle (int x, int y, int radius) +{ + this->x = x; + this->y = y; + this->radius = radius; +} + +/* + * Draw circle in view relative to point (x,y). + */ +void Circle::draw (core::View *view, core::style::Style *style, int x, int y) +{ + const bool filled = false; + + view->drawArc(style->color, core::style::Color::SHADING_NORMAL, filled, + x + this->x, y + this->y, 2 * this->radius, 2 * this->radius, + 0, 360); +} + +bool Circle::isPointWithin (int x, int y) +{ + return + (x - this->x) * (x - this->x) + (y - this->y) * (y - this->y) + <= radius * radius; +} + +// ---------------------------------------------------------------------- + +Polygon::Polygon () +{ + points = new misc::SimpleVector<Point> (8); + minx = miny = 0xffffff; + maxx = maxy = -0xffffff; +} + +Polygon::~Polygon () +{ + delete points; +} + +/* + * Draw polygon in view relative to point (x,y). + */ +void Polygon::draw (core::View *view, core::style::Style *style, int x, int y) +{ + if (points->size()) { + int i; + const bool filled = false, convex = false; + Point *pointArray = (Point *)malloc(points->size()*sizeof(struct Point)); + + for (i = 0; i < points->size(); i++) { + pointArray[i].x = x + points->getRef(i)->x; + pointArray[i].y = y + points->getRef(i)->y; + } + view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL, + filled, convex, pointArray, i); + free(pointArray); + } +} + +void Polygon::addPoint (int x, int y) +{ + points->increase (); + points->getRef(points->size () - 1)->x = x; + points->getRef(points->size () - 1)->y = y; + + minx = misc::min(minx, x); + miny = misc::min(miny, y); + maxx = misc::max(maxx, x); + maxy = misc::max(maxy, y); +} + +/** + * \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2), + * crosses the unlimited line, determined by two points (bx1, by1) and + * (bx2, by2). + */ +bool Polygon::linesCross0(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2) +{ + /** TODO Some more description */ + // If the scalar product is 0, it means that one point is on the second + // line, so we check for <= 0, not < 0. + int z1 = zOfVectorProduct (ax1 - bx1, ay1 - by1, bx2 - bx1, by2 - by1); + int z2 = zOfVectorProduct (ax2 - bx1, ay2 - by1, bx2 - bx1, by2 - by1); + + return (z1 <= 0 && z2 >= 0) || (z1 >= 0 && z2 <= 0); +} + +/** + * \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2), + * crosses the line, limited by (bx1, by1) and (bx2, by2). + */ +bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2) +{ + bool cross = + linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) && + linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2); + _MSG("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n", + ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No"); + return cross; +} + +bool Polygon::isPointWithin (int x, int y) +{ + if (points->size () < 3 || + (x < minx || x > maxx || y < miny || y >= maxy)) + return false; + else { + int numCrosses = 0; + for (int i = 0; i < points->size () - 1; i++) { + if (linesCross (minx - 1, miny - 1, x, y, + points->getRef(i)->x, points->getRef(i)->y, + points->getRef(i + 1)->x, points->getRef(i + 1)->y)) + numCrosses++; + } + if (linesCross (minx - 1, miny - 1, x, y, + points->getRef(points->size () - 1)->x, + points->getRef(points->size () - 1)->y, + points->getRef(0)->x, points->getRef(0)->y)) + numCrosses++; + + return numCrosses % 2 == 1; + } +} + +Region::Region() +{ + rectangleList = new container::typed::List <Rectangle> (true); +} + +Region::~Region() +{ + delete rectangleList; +} + +/** + * \brief Add a rectangle to the region and combine it with + * existing rectangles if possible. + * The number of rectangles is forced to be less than 16 + * by combining excessive rectangles. + */ +void Region::addRectangle (Rectangle *rPointer) +{ + container::typed::Iterator <Rectangle> it; + Rectangle *r = new Rectangle (rPointer->x, rPointer->y, + rPointer->width, rPointer->height); + + for (it = rectangleList->iterator (); it.hasNext (); ) { + Rectangle *ownRect = it.getNext (); + + int combinedHeight = + misc::max(r->y + r->height, ownRect->y + ownRect->height) - + misc::min(r->y, ownRect->y); + int combinedWidth = + misc::max(r->x + r->width, ownRect->x + ownRect->width) - + misc::min(r->x, ownRect->x); + + if (rectangleList->size() >= 16 || + combinedWidth * combinedHeight <= + ownRect->width * ownRect->height + r->width * r->height) { + + r->x = misc::min(r->x, ownRect->x); + r->y = misc::min(r->y, ownRect->y); + r->width = combinedWidth; + r->height = combinedHeight; + + rectangleList->removeRef (ownRect); + } + } + + rectangleList->append (r); +} + +Content::Type Content::maskForSelection (bool followReferences) +{ + Content::Type widgetMask = (Content::Type) + (Content::WIDGET_IN_FLOW | + (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT)); + return (Content::Type)(Content::SELECTION_CONTENT | widgetMask); +} + +void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb) +{ + switch(content->type) { + case START: + sb->append ("<start>"); + break; + case END: + sb->append ("<end>"); + break; + case TEXT: + sb->append ("\""); + sb->append (content->text); + sb->append ("\""); + break; + case WIDGET_IN_FLOW: + sb->append ("<widget in flow: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case WIDGET_OOF_REF: + sb->append ("<widget oof ref: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case WIDGET_OOF_CONT: + sb->append ("<widget oof cont: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case BREAK: + sb->append ("<break>"); + break; + default: + sb->append ("<"); + sb->appendInt (content->type); + sb->append ("?>"); + break; + } +} + +void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb) +{ + sb->append ((mask & START) ? "st" : "--"); + sb->append (":"); + sb->append ((mask & END) ? "en" : "--"); + sb->append (":"); + sb->append ((mask & TEXT) ? "tx" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--"); + sb->append (":"); + sb->append ((mask & BREAK) ? "br" : "--"); +} + +void Content::print (Content *content) +{ + misc::StringBuffer sb; + intoStringBuffer (content, &sb); + printf ("%s", sb.getChars ()); +} + +void Content::printMask (Type mask) +{ + misc::StringBuffer sb; + maskIntoStringBuffer (mask, &sb); + printf ("%s", sb.getChars ()); +} + +} // namespace core +} // namespace dw diff --git a/dw/types.hh b/dw/types.hh new file mode 100644 index 0000000..36d6caa --- /dev/null +++ b/dw/types.hh @@ -0,0 +1,238 @@ +#ifndef __DW_TYPES_HH__ +#define __DW_TYPES_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +namespace style { + class Style; +} + +enum HPosition +{ + HPOS_LEFT, + HPOS_CENTER, + HPOS_RIGHT, + HPOS_INTO_VIEW, /* scroll only, until the content in question comes + * into view */ + HPOS_NO_CHANGE +}; + +enum VPosition +{ + VPOS_TOP, + VPOS_CENTER, + VPOS_BOTTOM, + VPOS_INTO_VIEW, /* scroll only, until the content in question comes + * into view */ + VPOS_NO_CHANGE +}; + +enum ScrollCommand {SCREEN_UP_CMD, SCREEN_DOWN_CMD, SCREEN_LEFT_CMD, + SCREEN_RIGHT_CMD, LINE_UP_CMD, LINE_DOWN_CMD, + LEFT_CMD, RIGHT_CMD, TOP_CMD, BOTTOM_CMD}; + +/* + * Different "layers" may be highlighted in a widget. + */ +enum HighlightLayer +{ + HIGHLIGHT_SELECTION, + HIGHLIGHT_FINDTEXT, + HIGHLIGHT_NUM_LAYERS +}; + +struct Point +{ + int x; + int y; +}; + +/** + * \brief Abstract interface for different shapes. + */ +class Shape: public lout::object::Object +{ +public: + virtual bool isPointWithin (int x, int y) = 0; + virtual void draw (core::View *view, core::style::Style *style, int x, + int y) = 0; +}; + +/** + * \brief dw::core::Shape implemtation for simple rectangles. + */ +class Rectangle: public Shape +{ +public: + int x; + int y; + int width; + int height; + + inline Rectangle () { } + Rectangle (int x, int y, int width, int height); + + void draw (core::View *view, core::style::Style *style, int x, int y); + bool intersectsWith (Rectangle *otherRect, Rectangle *dest); + bool isSubsetOf (Rectangle *otherRect); + bool isPointWithin (int x, int y); + bool isEmpty () { return width <= 0 || height <= 0; }; +}; + +/** + * \brief dw::core::Shape implemtation for simple circles. + */ +class Circle: public Shape +{ +public: + int x, y, radius; + + Circle (int x, int y, int radius); + + void draw (core::View *view, core::style::Style *style, int x, int y); + bool isPointWithin (int x, int y); +}; + +/** + * \brief dw::core::Shape implemtation for polygons. + */ +class Polygon: public Shape +{ +private: + lout::misc::SimpleVector<Point> *points; + int minx, miny, maxx, maxy; + + /** + * \brief Return the z-coordinate of the vector product of two + * vectors, whose z-coordinate is 0 (so that x and y of + * the vector product is 0, too). + */ + inline int zOfVectorProduct(int x1, int y1, int x2, int y2) { + return x1 * y2 - x2 * y1; + } + + bool linesCross0(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2); + bool linesCross(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2); + +public: + Polygon (); + ~Polygon (); + + void draw (core::View *view, core::style::Style *style, int x, int y); + void addPoint (int x, int y); + bool isPointWithin (int x, int y); +}; + +/** + * Implementation for a point set. + * Currently represented as a set of rectangles not containing + * each other. + * It is guaranteed that the rectangles returned by rectangles () + * cover all rectangles that were added with addRectangle (). + */ +class Region +{ +private: + lout::container::typed::List <Rectangle> *rectangleList; + +public: + Region (); + ~Region (); + + void clear () { rectangleList->clear (); }; + + void addRectangle (Rectangle *r); + + lout::container::typed::Iterator <Rectangle> rectangles () + { + return rectangleList->iterator (); + }; +}; + +/** + * \brief Represents the allocation, i.e. actual position and size of a + * dw::core::Widget. + */ +struct Allocation +{ + int x; + int y; + int width; + int ascent; + int descent; +}; + +struct Requisition +{ + int width; + int ascent; + int descent; +}; + +struct Extremes +{ + int minWidth; + int maxWidth; + int minWidthIntrinsic; + int maxWidthIntrinsic; +}; + +struct Content +{ + enum Type { + START = 1 << 0, + END = 1 << 1, + TEXT = 1 << 2, + + /** \brief widget in normal flow, so that _this_ widget + (containing this content) is both container (parent) and + generator */ + WIDGET_IN_FLOW = 1 << 3, + + /** \brief widget out of flow (OOF); _this_ widget (containing + this content) is only the container (parent), but _not_ + generator */ + WIDGET_OOF_CONT = 1 << 4, + + /** \brief reference to a widget out of flow (OOF); _this_ + widget (containing this content) is only the generator + (parent), but _not_ container */ + WIDGET_OOF_REF = 1 << 5, + BREAK = 1 << 6, + + ALL = 0xff, + REAL_CONTENT = 0xff ^ (START | END), + SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally + ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF, + }; + + /* Content is embedded in struct Word therefore we + * try to be space efficient. + */ + short type; + bool space; + union { + const char *text; + Widget *widget; + int breakSpace; + }; + + static Content::Type maskForSelection (bool followReferences); + + static void intoStringBuffer(Content *content, lout::misc::StringBuffer *sb); + static void maskIntoStringBuffer(Type mask, lout::misc::StringBuffer *sb); + static void print (Content *content); + static void printMask (Type mask); +}; + +} // namespace core +} // namespace dw + +#endif // __DW_TYPES_HH__ diff --git a/dw/ui.cc b/dw/ui.cc new file mode 100644 index 0000000..c021d49 --- /dev/null +++ b/dw/ui.cc @@ -0,0 +1,519 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include "../lout/debug.hh" + +#include <stdio.h> + +namespace dw { +namespace core { +namespace ui { + +using namespace lout; +using namespace lout::object; + +int Embed::CLASS_ID = -1; + +Embed::Embed(Resource *resource) +{ + DBG_OBJ_CREATE ("dw::core::ui::Embed"); + registerName ("dw::core::ui::Embed", &CLASS_ID); + this->resource = resource; + resource->setEmbed (this); + DBG_OBJ_ASSOC_CHILD (resource); +} + +Embed::~Embed() +{ + delete resource; + DBG_OBJ_DELETE (); +} + +void Embed::sizeRequestImpl (Requisition *requisition) +{ + resource->sizeRequest (requisition); +} + +void Embed::getExtremesImpl (Extremes *extremes) +{ + resource->getExtremes (extremes); + correctExtremes (extremes); +} + +void Embed::sizeAllocateImpl (Allocation *allocation) +{ + resource->sizeAllocate (allocation); +} + +int Embed::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + return resource->getAvailWidthOfChild (child, forceValue); +} + +int Embed::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + return resource->getAvailHeightOfChild (child, forceValue); +} + +void Embed::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + resource->correctRequisitionOfChild (child, requisition, splitHeightFun); +} + +void Embed::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + resource->correctExtremesOfChild (child, extremes); +} + +void Embed::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + resource->containerSizeChangedForChildren (); + DBG_OBJ_LEAVE (); +} + +void Embed::enterNotifyImpl (core::EventCrossing *event) +{ + resource->emitEnter(); + Widget::enterNotifyImpl(event); +} + +void Embed::leaveNotifyImpl (core::EventCrossing *event) +{ + resource->emitLeave(); + Widget::leaveNotifyImpl(event); +} + +bool Embed::buttonPressImpl (core::EventButton *event) +{ + bool handled; + + if (event->button == 3) { + resource->emitClicked(event); + handled = true; + } else { + handled = false; + } + return handled; +} + +void Embed::setDisplayed (bool displayed) +{ + resource->setDisplayed (displayed); +} + +void Embed::setEnabled (bool enabled) +{ + resource->setEnabled (enabled); +} + +void Embed::draw (View *view, Rectangle *area) +{ + drawWidgetBox (view, area, false); + resource->draw (view, area); +} + +Iterator *Embed::iterator (Content::Type mask, bool atEnd) +{ + return resource->iterator (mask, atEnd); +} + +void Embed::setStyle (style::Style *style) +{ + resource->setStyle (style); + Widget::setStyle (style); +} + +// ---------------------------------------------------------------------- + +bool Resource::ActivateEmitter::emitToReceiver (lout::signal::Receiver + *receiver, + int signalNo, + int argc, Object **argv) +{ + ActivateReceiver *ar = (ActivateReceiver*)receiver; + Resource *res = (Resource*)((Pointer*)argv[0])->getValue (); + + switch (signalNo) { + case 0: + ar->activate (res); + break; + case 1: + ar->enter (res); + break; + case 2: + ar->leave (res); + break; + default: + misc::assertNotReached (); + } + return false; +} + +void Resource::ActivateEmitter::emitActivate (Resource *resource) +{ + Pointer p (resource); + Object *argv[1] = { &p }; + emitVoid (0, 1, argv); +} + +void Resource::ActivateEmitter::emitEnter (Resource *resource) +{ + Pointer p (resource); + Object *argv[1] = { &p }; + emitVoid (1, 1, argv); +} + +void Resource::ActivateEmitter::emitLeave (Resource *resource) +{ + Pointer p (resource); + Object *argv[1] = { &p }; + emitVoid (2, 1, argv); +} + +// ---------------------------------------------------------------------- + +Resource::~Resource () +{ + DBG_OBJ_DELETE (); +} + +void Resource::setEmbed (Embed *embed) +{ + this->embed = embed; +} + +void Resource::getExtremes (Extremes *extremes) +{ + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); + + /* Simply return the requisition width */ + Requisition requisition; + sizeRequest (&requisition); + extremes->minWidth = extremes->maxWidth = requisition.width; + extremes->minWidthIntrinsic = extremes->minWidth; + extremes->maxWidthIntrinsic = extremes->maxWidth; + + DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +void Resource::sizeAllocate (Allocation *allocation) +{ +} + +int Resource::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); + return 0; +} + +int Resource::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); + return 0; +} + +void Resource::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); +} + +void Resource::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); +} + +void Resource::containerSizeChangedForChildren () +{ + // No children by default. +} + +void Resource::setDisplayed (bool displayed) +{ +} + +void Resource::draw (View *view, Rectangle *area) +{ +} + +void Resource::setStyle (style::Style *style) +{ +} + +void Resource::emitEnter () +{ + activateEmitter.emitEnter(this); +} + +void Resource::emitLeave () +{ + activateEmitter.emitLeave(this); +} + +bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver, + int signalNo, int argc, + Object **argv) +{ + ((ClickedReceiver*)receiver) + ->clicked ((Resource*)((Pointer*)argv[0])->getValue (), + (EventButton*)((Pointer*)argv[1])->getValue()); + return false; +} + +void Resource::ClickedEmitter::emitClicked (Resource *resource, + EventButton *event) +{ + Pointer p1 (resource); + Pointer p2 (event); + Object *argv[2] = { &p1, &p2 }; + emitVoid (0, 2, argv); +} + +// ---------------------------------------------------------------------- + +Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd) +{ + /** \todo Perhaps in brackets? */ + // return new TextIterator (getEmbed (), mask, atEnd, getLabel ()); + /** \bug Not implemented. */ + return new EmptyIterator (getEmbed (), mask, atEnd); +} + +// ---------------------------------------------------------------------- + +void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged) +{ + DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver/resizeQueued", "%s", + extremesChanged ? "true" : "false"); + resource->queueResize (extremesChanged); + DBG_OBJ_LEAVE (); +} + +ComplexButtonResource::ComplexButtonResource () +{ + DBG_OBJ_CREATE ("dw::core::ui::ComplexButtonResource"); + layout = NULL; + layoutReceiver.resource = this; + click_x = click_y = -1; +} + +void ComplexButtonResource::init (Widget *widget) +{ + childWidget = widget; + + layout = new Layout (createPlatform ()); + setLayout (layout); + DBG_OBJ_ASSOC_CHILD (layout); + layout->setWidget (widget); + layout->connect (&layoutReceiver); + + if (getEmbed ()) + childWidget->setQuasiParent (getEmbed ()); +} + +void ComplexButtonResource::setEmbed (Embed *embed) +{ + ButtonResource::setEmbed (embed); + + if (childWidget) + childWidget->setQuasiParent (getEmbed ()); +} + +ComplexButtonResource::~ComplexButtonResource () +{ + delete layout; + DBG_OBJ_DELETE (); +} + +void ComplexButtonResource::sizeRequest (Requisition *requisition) +{ + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + Requisition widgetRequisition; + childWidget->sizeRequest (&widgetRequisition); + requisition->width = widgetRequisition.width + 2 * reliefXThickness (); + requisition->ascent = widgetRequisition.ascent + reliefYThickness (); + requisition->descent = widgetRequisition.descent + reliefYThickness (); + + DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +void ComplexButtonResource::getExtremes (Extremes *extremes) +{ + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); + + Extremes widgetExtremes; + childWidget->getExtremes (&widgetExtremes); + extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness (); + extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness (); + extremes->minWidthIntrinsic = extremes->minWidth; + extremes->maxWidthIntrinsic = extremes->maxWidth; + + DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +void ComplexButtonResource::sizeAllocate (Allocation *allocation) +{ +} + +int ComplexButtonResource::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + int embedWidth = getEmbed()->getAvailWidth (forceValue); + if (embedWidth == -1) + return -1; + else + return misc::max (embedWidth - 2 * reliefXThickness (), 0); +} + +int ComplexButtonResource::getAvailHeightOfChild (Widget *child, + bool forceValue) +{ + int embedHeight = getEmbed()->getAvailHeight (forceValue); + if (embedHeight == -1) + return -1; + else + return misc::max (embedHeight - 2 * reliefYThickness (), 0); +} + +void ComplexButtonResource::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) + (int, int*, int*)) +{ + // Similar to Widget::correctRequisitionOfChild, but for percentage + // the relief has to be considered. + + if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getEmbed()->getAvailHeight (false); + if (availWidth != -1) { + int baseWidth = misc::max (availWidth + - getEmbed()->boxDiffWidth () + - 2 * reliefXThickness (), + 0); + requisition->width = + child->applyPerWidth (baseWidth, child->getStyle()->width); + } + } else + getEmbed()->correctReqWidthOfChildNoRec (child, requisition); + + // TODO Percentage heights are ignored again. + getEmbed()->correctReqHeightOfChildNoRec (child, requisition, + splitHeightFun); + +} + +void ComplexButtonResource::correctExtremesOfChild (Widget *child, + Extremes *extremes) +{ + // Similar to Widget::correctExtremesOfChild, but for percentage + // the relief has to be considered. + + if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getEmbed()->getAvailHeight (false); + if (availWidth != -1) { + int baseWidth = misc::max (availWidth + - getEmbed()->boxDiffWidth () + - 2 * reliefXThickness (), + 0); + extremes->minWidth = extremes->maxWidth = + child->applyPerWidth (baseWidth, child->getStyle()->width); + } + } else + getEmbed()->correctExtremesOfChildNoRec (child, extremes); +} + +void ComplexButtonResource::containerSizeChangedForChildren () +{ + layout->containerSizeChanged (); +} + +Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd) +{ + /** + * \bug Implementation. + * This is a bit more complicated: We have two layouts here. + */ + return new EmptyIterator (getEmbed (), mask, atEnd); +} + +// ---------------------------------------------------------------------- + +Iterator *TextResource::iterator (Content::Type mask, bool atEnd) +{ + // return new TextIterator (getEmbed (), mask, atEnd, getText ()); + /** \bug Not implemented. */ + return new EmptyIterator (getEmbed (), mask, atEnd); +} + +// ---------------------------------------------------------------------- + +Iterator *CheckButtonResource::iterator (Content::Type mask, bool atEnd) +{ + //return new TextIterator (getEmbed (), mask, atEnd, + // isActivated () ? "[X]" : "[ ]"); + /** \bug Not implemented. */ + return new EmptyIterator (getEmbed (), mask, atEnd); +} + +// ---------------------------------------------------------------------- + +RadioButtonResource::GroupIterator::~GroupIterator () +{ +} + +Iterator *RadioButtonResource::iterator (Content::Type mask, bool atEnd) +{ + //return new TextIterator (getEmbed (), mask, atEnd, + // isActivated () ? "(*)" : "( )"); + /** \bug Not implemented. */ + return new EmptyIterator (getEmbed (), mask, atEnd); +} + +} // namespace ui +} // namespace core +} // namespace dw + diff --git a/dw/ui.hh b/dw/ui.hh new file mode 100644 index 0000000..6703ccc --- /dev/null +++ b/dw/ui.hh @@ -0,0 +1,592 @@ +#ifndef __DW_UI_HH__ +#define __DW_UI_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief Anything related to embedded UI widgets is defined here. + * + * UI resources are another abstraction for Dw widgets, which are not + * fully implemented in a platform-independent way. Typically, they + * involve creating widgets, which the underlying UI toolkit provides. + * + * As you see in this diagram: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", dir="both", + * labelfontname=Helvetica, labelfontsize=10, color="#404040", + * labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_core { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::core"; + * + * subgraph cluster_ui { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::core::ui"; + * + * Embed [URL="\ref dw::core::ui::Embed"]; + * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"]; + * LabelButtonResource [color="#a0a0a0", + * URL="\ref dw::core::ui::LabelButtonResource"]; + * EntryResource [color="#a0a0a0", + * URL="\ref dw::core::ui::EntryResource"]; + * etc [color="#a0a0a0", label="..."]; + * } + * + * Widget [URL="\ref dw::core::Widget", color="#a0a0a0"]; + * } + * + * subgraph cluster_fltk { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="dw::fltk::ui"; + * + * FltkLabelButtonResource + * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"]; + * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"]; + * } + * + * Widget -> Embed; + * Embed -> Resource [arrowhead="open", arrowtail="none", + * headlabel="1", taillabel="1"]; + * Resource -> LabelButtonResource; + * Resource -> EntryResource; + * Resource -> etc; + * LabelButtonResource -> FltkLabelButtonResource; + * EntryResource -> FltkEntryResource; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * there are several levels: + * + * <ol> + * <li> The Dw widget is dw::core::ui::Embed. It delegates most to + * dw::core::ui::Resource, which has similar methods like + * dw::core::Widget. + * + * <li> There are several sub interfaces of dw::core::ui::Resource, which + * may provide methods, as e.g. dw::core::ui::ListResource::addItem. In a + * platform independent context, you can cast the result of + * dw::core::ui::Embed::getResource to a specific sub class, if you + * know, which one is used. E.g., if you know, that a given instance + * dw::core::ui::Embed refers to a dw::core::ui::ListResource, you can + * write something like: + * + * \code + * dw::core::ui::Embed *embed; + * //... + * ((dw::core::ui::ListResource*)embed->getResource ())->addItem ("Hello!"); + * \endcode + * + * <li> These sub classes are then fully implemented in a platform specific + * way. For an example, look at dw::fltk::ui. + * </ol> + * + * There is a factory interface, dw::core::ui::ResourceFactory, which + * provides methods for creating common resources. By calling + * dw::core::Layout::getResourceFactory, which calls + * dw::core::Platform::getResourceFactory, you get the factory for the used + * platform. + * + * It is possible to define additional sub classes of + * dw::core::ui::Resource, but since they are not provided by + * dw::core::ui::ResourceFactory, you have to define some other + * abstractions, if you want to remain platform independent. + * + * + * <h3>...</h3> + * + * + * <h3>Resouces needed for HTML</h3> + * + * This chapter describes, how the form controls defined by HTML are + * implemented in Dw. Some of them do not refer to UI resources, but to + * other widgets, links to the respective documentations are provided + * here. + * + * <h4>Resouces created with \<INPUT\></h4> + * + * The HTML \<INPUT\> is always implemented by using UI + * resources. \<INPUT\> element has the following attributes: + * + * <table> + * <tr><th>Attribute <th>Implementation + * <tr><td>type <td>This defines the resource you have to instantiate. + * <tr><td>name <td>Not needed within Dw. + * <tr><td>value <td>The initial value is treated differently by different + * resources. + * <tr><td>checked <td>Parameter to + * dw::core::ui::ResourceFactory::createCheckButtonResource + * and dw::core::ui::ResourceFactory::createRadioButtonResource. + * <tr><td>disabled <td>This is provided for all resources by + * dw::core::ui::Resource::setEnabled. + * <tr><td>readonly <td>This is provided by + * dw::core::ui::TextResource::setEditable. + * <tr><td>size <td>This is handled by styles. + * <tr><td>maxlength <td>Parameter of + * dw::core::ui::ResourceFactory::createEntryResource. + * <tr><td>src <td>Handled by the caller (HTML parser). + * <tr><td>alt <td>Handled by the caller (HTML parser). + * <tr><td>usemap <td>Handled by the caller (HTML parser). + * <tr><td>ismap <td>Handled by the caller (HTML parser). + * <tr><td>tabindex <td>Not supported currently. + * <tr><td>accesskey <td>Not supported currently. + * <tr><td>onfocus <td>Not supported currently. + * <tr><td>onblur <td>Not supported currently. + * <tr><td>onselect <td>Not supported currently. + * <tr><td>onchange <td>Not supported currently. + * <tr><td>accept <td>Not supported currently. + * </table> + * + * For the different values of \em type, the following resources can be + * used: + * + * <table> + * <tr><th>Type <th>Resource + * <th>Factory Method + * <tr><td>text <td>dw::core::ui::EntryResource + * <td>dw::core::ui::ResourceFactory::createEntryResource + * <tr><td>password <td>dw::core::ui::EntryResource + * <td>dw::core::ui::ResourceFactory::createEntryResource + * <tr><td>checkbox <td>dw::core::ui::CheckButtonResource + * <td>dw::core::ui::ResourceFactory::createCheckButtonResource + * <tr><td>radio <td>dw::core::ui::RadioButtonResource + * <td>dw::core::ui::ResourceFactory::createRadioButtonResource + * <tr><td>submit <td>dw::core::ui::LabelButtonResource + * <td>dw::core::ui::ResourceFactory::createLabelButtonResource + * <tr><td>image <td>dw::core::ui::ComplexButtonResource + * <td>dw::core::ui::ResourceFactory::createComplexButtonResource, + * width a dw::Image inside and relief = false. + * <tr><td>reset <td>dw::core::ui::LabelButtonResource + * <td>dw::core::ui::ResourceFactory::createLabelButtonResource + * <tr><td>button <td>dw::core::ui::LabelButtonResource + * <td>dw::core::ui::ResourceFactory::createLabelButtonResource + * <tr><td>hidden <td>No rendering necessary. + * <td>- + * <tr><td>file <td>Not supported currently. + * <td>- + * </table> + * + * <h4>\<SELECT\>, \<OPTGROUP\>, and \<OPTION\></h4> + * + * \<SELECT\> is implemented either by dw::core::ui::OptionMenuResource + * (better suitable for \em size = 1 and single selection) or + * dw::core::ui::ListResource, which have a common base, + * dw::core::ui::SelectionResource. In the latter case, \em size must be + * specified via dw::core::style::Style. + * + * Factory methods are dw::core::ui::ResourceFactory::createListResource and + * dw::core::ui::ResourceFactory::createOptionMenuResource. + * + * \<OPTION\>'s are added via dw::core::ui::SelectionResource::addItem. + * + * \<OPTGROUP\> are created by using dw::core::ui::SelectionResource::pushGroup + * and dw::core::ui::SelectionResource::popGroup. + * + * For lists, the selection mode must be set in + * dw::core::ui::ResourceFactory::createListResource. + * + * <h4>\<TEXTAREA\></h4> + * + * \<TEXTAREA\> is implemented by dw::core::ui::MultiLineTextResource, + * the factory method is + * dw::core::ui::ResourceFactory::createMultiLineTextResource. + * dw::core::ui::TextResource::setEditable can be used, as for entries. + * + * <h4>\<BUTTON\></h4> + * + * For handling \<BUTTON\>, dw::core::ui::ComplexButtonResource should be used, + * with a dw::Textblock inside, and relief = true. The contents of \<BUTTON\> + * is then added to the dw::Textblock. + * + * \todo describe activation signal + */ +namespace ui { + +class Resource; + +/** + * \brief A widget for embedding UI widgets. + * + * \sa dw::core::ui + */ +class Embed: public Widget +{ + friend class Resource; + +private: + Resource *resource; + +protected: + void sizeRequestImpl (Requisition *requisition); + void getExtremesImpl (Extremes *extremes); + void sizeAllocateImpl (Allocation *allocation); + + int getAvailWidthOfChild (Widget *child, bool forceValue); + int getAvailHeightOfChild (Widget *child, bool forceValue); + void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesOfChild (Widget *child, Extremes *extremes); + + void containerSizeChangedForChildren (); + + void enterNotifyImpl (core::EventCrossing *event); + void leaveNotifyImpl (core::EventCrossing *event); + bool buttonPressImpl (core::EventButton *event); + +public: + static int CLASS_ID; + + Embed(Resource *resource); + ~Embed(); + + void setDisplayed (bool displayed); + void setEnabled (bool enabled); + void draw (View *view, Rectangle *area); + Iterator *iterator (Content::Type mask, bool atEnd); + void setStyle (style::Style *style); + + inline Resource *getResource () { return resource; } + + inline void correctReqWidthOfChildNoRec (Widget *child, + Requisition *requisition) + { Widget::correctReqWidthOfChild (child, requisition); } + + inline void correctReqHeightOfChildNoRec (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) + { Widget::correctReqHeightOfChild (child, requisition, splitHeightFun); } + + virtual void correctExtremesOfChildNoRec (Widget *child, Extremes *extremes) + { Widget::correctExtremesOfChild (child, extremes); } +}; + +/** + * \brief Basic interface for all resources. + * + * \sa dw::core::ui + */ +class Resource +{ + friend class Embed; + +public: + /** + * \brief Receiver interface for the "activate" signal. + */ + class ActivateReceiver: public lout::signal::Receiver + { + public: + virtual void activate (Resource *resource) = 0; + virtual void enter (Resource *resource) = 0; + virtual void leave (Resource *resource) = 0; + }; + /** + * \brief Receiver interface for the "clicked" signal. + */ + class ClickedReceiver: public lout::signal::Receiver + { + public: + virtual void clicked (Resource *resource, EventButton *event) = 0; + }; + +private: + class ActivateEmitter: public lout::signal::Emitter + { + protected: + bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, + int argc, Object **argv); + public: + inline void connectActivate (ActivateReceiver *receiver) { + connect (receiver); } + void emitActivate (Resource *resource); + void emitEnter (Resource *resource); + void emitLeave (Resource *resource); + }; + + class ClickedEmitter: public lout::signal::Emitter + { + protected: + bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, + int argc, Object **argv); + public: + inline void connectClicked (ClickedReceiver *receiver) { + connect (receiver); } + void emitClicked (Resource *resource, EventButton *event); + }; + + Embed *embed; + ActivateEmitter activateEmitter; + ClickedEmitter clickedEmitter; + + void emitEnter (); + void emitLeave (); +protected: + inline void queueResize (bool extremesChanged) { + if (embed) embed->queueResize (0, extremesChanged); + } + + virtual Embed *getEmbed () { return embed; } + virtual void setEmbed (Embed *embed); + + inline void emitActivate () { + return activateEmitter.emitActivate (this); } + inline void emitClicked (EventButton *event) { + clickedEmitter.emitClicked (this, event); } + +public: + inline Resource () + { embed = NULL; DBG_OBJ_CREATE ("dw::core::ui::Resource"); } + + virtual ~Resource (); + + virtual void sizeRequest (Requisition *requisition) = 0; + virtual void getExtremes (Extremes *extremes); + virtual void sizeAllocate (Allocation *allocation); + + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + virtual void containerSizeChangedForChildren (); + + virtual void setDisplayed (bool displayed); + virtual void draw (View *view, Rectangle *area); + virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void setStyle (style::Style *style); + + virtual bool isEnabled () = 0; + virtual void setEnabled (bool enabled) = 0; + + inline void connectActivate (ActivateReceiver *receiver) { + activateEmitter.connectActivate (receiver); } + inline void connectClicked (ClickedReceiver *receiver) { + clickedEmitter.connectClicked (receiver); } +}; + + +class ButtonResource: public Resource +{}; + +/** + * \brief Interface for labelled buttons resources. + */ +class LabelButtonResource: public ButtonResource +{ +public: + Iterator *iterator (Content::Type mask, bool atEnd); + + virtual const char *getLabel () = 0; + virtual void setLabel (const char *label) = 0; +}; + +class ComplexButtonResource: public ButtonResource +{ +private: + class LayoutReceiver: public Layout::Receiver + { + public: + ComplexButtonResource *resource; + + void resizeQueued (bool extremesChanged); + }; + + friend class LayoutReceiver; + LayoutReceiver layoutReceiver; + + Widget *childWidget; + +protected: + Layout *layout; + int click_x, click_y; + + void setEmbed (Embed *embed); + + virtual Platform *createPlatform () = 0; + virtual void setLayout (Layout *layout) = 0; + + virtual int reliefXThickness () = 0; + virtual int reliefYThickness () = 0; + + void init (Widget *widget); + +public: + ComplexButtonResource (); + ~ComplexButtonResource (); + + void sizeRequest (Requisition *requisition); + void getExtremes (Extremes *extremes); + void sizeAllocate (Allocation *allocation); + + int getAvailWidthOfChild (Widget *child, bool forceValue); + int getAvailHeightOfChild (Widget *child, bool forceValue); + void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesOfChild (Widget *child, Extremes *extremes); + void containerSizeChangedForChildren (); + + Iterator *iterator (Content::Type mask, bool atEnd); + int getClickX () {return click_x;}; + int getClickY () {return click_y;}; +}; + +/** + * \brief Base interface for dw::core::ui::ListResource and + * dw::core::ui::OptionMenuResource. + */ +class SelectionResource: public Resource +{ +public: + virtual void addItem (const char *str, bool enabled, bool selected) = 0; + virtual void setItem (int index, bool selected) = 0; + virtual void pushGroup (const char *name, bool enabled) = 0; + virtual void popGroup () = 0; + + virtual int getNumberOfItems () = 0; + virtual bool isSelected (int index) = 0; +}; + +class ListResource: public SelectionResource +{ +public: + enum SelectionMode { + /** + * \brief Exactly one item is selected. + * + * If no item is selected initially, the first one is selected. + */ + SELECTION_EXACTLY_ONE, + + /** + * \brief Exactly one item is selected, except possibly at the beginning. + * + * If no item is selected initially, no one is selected automatically. + * The user may not unselect the only selected item. + */ + SELECTION_EXACTLY_ONE_BY_USER, + + /** + * \brief At most one item is selected. + * + * If no item is selected initially, no one is selected automatically. + * The user may unselect the only selected item. + */ + SELECTION_AT_MOST_ONE, + + /** + * \brief An arbitrary number of items may be selected. + */ + SELECTION_MULTIPLE + }; +}; + +class OptionMenuResource: public SelectionResource +{ +}; + +class TextResource: public Resource +{ +public: + Iterator *iterator (Content::Type mask, bool atEnd); + + virtual const char *getText () = 0; + virtual void setText (const char *text) = 0; + virtual bool isEditable () = 0; + virtual void setEditable (bool editable) = 0; +}; + +class EntryResource: public TextResource +{ +public: + enum { UNLIMITED_SIZE = -1 }; + virtual void setMaxLength (int maxlen) = 0; +}; + +class MultiLineTextResource: public TextResource +{ +}; + + +class ToggleButtonResource: public Resource +{ +public: + virtual bool isActivated () = 0; + virtual void setActivated (bool activated) = 0; +}; + +class CheckButtonResource: public ToggleButtonResource +{ +public: + Iterator *iterator (Content::Type mask, bool atEnd); +}; + +class RadioButtonResource: public ToggleButtonResource +{ +public: + class GroupIterator + { + protected: + GroupIterator () { } + virtual ~GroupIterator (); + + public: + virtual bool hasNext () = 0; + virtual RadioButtonResource *getNext () = 0; + virtual void unref () = 0; + }; + + /** + * \brief Return an iterator, to access all radio button resources + * within the group. + */ + virtual GroupIterator *groupIterator () = 0; + + Iterator *iterator (Content::Type mask, bool atEnd); +}; + + +/** + * \brief A factory for the common resource. + */ +class ResourceFactory: public lout::object::Object +{ +public: + virtual LabelButtonResource *createLabelButtonResource (const char *label) + = 0; + virtual ComplexButtonResource *createComplexButtonResource (Widget *widget, + bool relief) + = 0; + virtual ListResource *createListResource (ListResource::SelectionMode + selectionMode, int rows) = 0; + virtual OptionMenuResource *createOptionMenuResource () = 0; + virtual EntryResource *createEntryResource (int size, bool password, + const char *label) = 0; + virtual MultiLineTextResource *createMultiLineTextResource (int cols, + int rows) = 0; + virtual CheckButtonResource *createCheckButtonResource (bool activated) = 0; + virtual RadioButtonResource *createRadioButtonResource (RadioButtonResource + *groupedWith, + bool activated) = 0; +}; + +} // namespace ui +} // namespace core +} // namespace dw + +#endif // __DW_UI_HH__ diff --git a/dw/view.hh b/dw/view.hh new file mode 100644 index 0000000..8037dc6 --- /dev/null +++ b/dw/view.hh @@ -0,0 +1,211 @@ +#ifndef __DW_VIEW_HH__ +#define __DW_VIEW_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief An interface to encapsulate platform dependent drawing. + * + * \sa\ref dw-overview, \ref dw-layout-views + */ +class View: public lout::object::Object +{ +public: + /* + * ---------------------------- + * Operations on the view + * ---------------------------- + */ + + /** + * \brief This methods notifies the view, that it has been attached to a + * layout. + */ + virtual void setLayout (Layout *layout) = 0; + + /** + * \brief Set the canvas size. + */ + virtual void setCanvasSize (int width, int ascent, int descent) = 0; + + /** + * \brief Set the cursor appearance. + */ + virtual void setCursor (style::Cursor cursor) = 0; + + /** + * \brief Set the background of the view. + */ + virtual void setBgColor (style::Color *color) = 0; + + /* + * --------------------------------------------------------- + * Scrolling and Related. Only usesViewport must be + * implemented, if it returns false, the other methods + * are never called. + * ------------------------------------------------------- + */ + + /** + * \brief Return, whether this view uses a viewport. + */ + virtual bool usesViewport () = 0; + + /** + * \brief Get the thickness of the horizontal scrollbar, when it is + * visible. + * + * Does not have to be implemented, when usesViewport returns false. + */ + virtual int getHScrollbarThickness () = 0; + + /** + * \brief Get the thickness of the vertical scrollbar, when it is + * visible. + * + * Does not have to be implemented, when usesViewport returns false. + */ + virtual int getVScrollbarThickness () = 0; + + /** + * \brief Scroll the vieport to the given position. + * + * Does not have to be implemented, when usesViewport returns false. + */ + virtual void scrollTo (int x, int y) = 0; + + /** + * \brief Scroll the viewport as commanded. + */ + virtual void scroll (ScrollCommand) { }; + + /** + * \brief Set the viewport size. + * + * Does not have to be implemented, when usesViewport returns false. + * + * This will normally imply a resize of the UI widget. Width and height are + * the dimensions of the new size, \em including the scrollbar thicknesses. + * + */ + virtual void setViewportSize (int width, int height, + int hScrollbarThickness, + int vScrollbarThickness) = 0; + + /* + * ----------------------- + * Drawing functions + * ----------------------- + */ + + /** + * \brief Called before drawing. + * + * All actual drawing operations will be enclosed into calls of + * dw::core:View::startDrawing and dw::core:View::finishDrawing. They + * may be implemented, e.g. when a backing + * pixmap is used, to prevent flickering. StartDrawing() will then + * initialize the backing pixmap, all other drawing operations will draw + * into it, and finishDrawing() will merge it into the window. + */ + virtual void startDrawing (Rectangle *area) = 0; + + /** + * \brief Called after drawing. + * + * \sa dw::core:View::startDrawing + */ + virtual void finishDrawing (Rectangle *area) = 0; + + /** + * \brief Queue a region, which is given in \em canvas coordinates, for + * drawing. + * + * The view implementation is responsible, that this region is drawn, either + * immediately, or (which is more typical, since more efficient) the areas + * are collected, combined (as far as possible), and the drawing is later + * done in an idle function. + */ + virtual void queueDraw (Rectangle *area) = 0; + + /** + * \brief Queue the total viewport for drawing. + * + * \sa dw::core::View::queueDraw + */ + virtual void queueDrawTotal () = 0; + + /** + * \brief Cancel a draw queue request. + * + * If dw::core::View::queueDraw or dw::core::View::queueDrawTotal have been + * called before, and the actual drawing was not processed yet, the actual + * drawing should be cancelled. Otherwise, the cancellation should be + * ignored. + */ + virtual void cancelQueueDraw () = 0; + + /* + * The following methods should be self-explaining. + */ + + virtual void drawPoint (style::Color *color, + style::Color::Shading shading, + int x, int y) = 0; + virtual void drawLine (style::Color *color, + style::Color::Shading shading, + int x1, int y1, int x2, int y2) = 0; + virtual void drawTypedLine (style::Color *color, + style::Color::Shading shading, + style::LineType type, int width, + int x1, int y1, int x2, int y2) = 0; + virtual void drawRectangle (style::Color *color, + style::Color::Shading shading, bool filled, + int x, int y, int width, int height) = 0; + virtual void drawArc (style::Color *color, + style::Color::Shading shading, bool filled, + int centerX, int centerY, int width, int height, + int angle1, int angle2) = 0; + virtual void drawPolygon (style::Color *color, + style::Color::Shading shading, + bool filled, bool convex, Point *points, + int npoints) = 0; + virtual void drawText (style::Font *font, + style::Color *color, + style::Color::Shading shading, + int x, int y, const char *text, int len) = 0; + virtual void drawSimpleWrappedText (style::Font *font, style::Color *color, + style::Color::Shading shading, + int x, int y, int w, int h, + const char *text) = 0; + virtual void drawImage (Imgbuf *imgbuf, int xRoot, int yRoot, + int x, int y, int width, int height) = 0; + + /* + * -------------- + * Clipping + * -------------- + */ + + /* + * To prevent drawing outside of a given area, a clipping view may be + * requested, which also implements this interface. The clipping view is + * related to the parent view (clipping views may be nested!), anything + * which is drawn into this clipping view, is later merged again into the + * parent view. An implementation will typically use additional pixmaps, + * which are later merged into the parent view pixmap/window. + */ + + virtual View *getClippingView (int x, int y, int width, int height) = 0; + virtual void mergeClippingView (View *clippingView) = 0; +}; + +} // namespace core +} // namespace dw + +#endif // __DW_VIEW_HH__ diff --git a/dw/widget.cc b/dw/widget.cc new file mode 100644 index 0000000..4ebce30 --- /dev/null +++ b/dw/widget.cc @@ -0,0 +1,1785 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "core.hh" + +#include "../lout/msg.h" +#include "../lout/debug.hh" + +using namespace lout; +using namespace lout::object; + +namespace dw { +namespace core { + +// ---------------------------------------------------------------------- + +bool Widget::WidgetImgRenderer::readyToDraw () +{ + return widget->wasAllocated (); +} + +void Widget::WidgetImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + widget->getPaddingArea (x, y, width, height); +} + +void Widget::WidgetImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) +{ + widget->getPaddingArea (xRef, yRef, widthRef, heightRef); +} + +style::Style *Widget::WidgetImgRenderer::getStyle () +{ + return widget->getStyle (); +} + +void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height) +{ + widget->queueDrawArea (x - widget->allocation.x, y - widget->allocation.y, + width, height); +} + +// ---------------------------------------------------------------------- + +bool Widget::adjustMinWidth = false; +int Widget::CLASS_ID = -1; + +Widget::Widget () +{ + DBG_OBJ_CREATE ("dw::core::Widget"); + registerName ("dw::core::Widget", &CLASS_ID); + + flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED); + parent = quasiParent = generator = container = NULL; + DBG_OBJ_SET_PTR ("container", container); + + layout = NULL; + + allocation.x = -1; + allocation.y = -1; + allocation.width = 1; + allocation.ascent = 1; + allocation.descent = 0; + + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + + style = NULL; + bgColor = NULL; + buttonSensitive = true; + buttonSensitiveSet = false; + + deleteCallbackData = NULL; + deleteCallbackFunc = NULL; + + widgetImgRenderer = NULL; +} + +Widget::~Widget () +{ + if (deleteCallbackFunc) + deleteCallbackFunc (deleteCallbackData); + + if (widgetImgRenderer) { + if (style && style->backgroundImage) + style->backgroundImage->removeExternalImgRenderer (widgetImgRenderer); + delete widgetImgRenderer; + } + + if (style) + style->unref (); + + if (parent) + parent->removeChild (this); + else if (layout) + layout->removeWidget (); + + DBG_OBJ_DELETE (); +} + + +/** + * \brief Calculates the intersection of widget->allocation and area, returned + * in intersection (in widget coordinates!). + * + * Typically used by containers when + * drawing their children. Returns whether intersection is not empty. + */ +bool Widget::intersects (Rectangle *area, Rectangle *intersection) +{ + Rectangle parentArea, childArea; + + parentArea = *area; + parentArea.x += parent->allocation.x; + parentArea.y += parent->allocation.y; + + childArea.x = allocation.x; + childArea.y = allocation.y; + childArea.width = allocation.width; + childArea.height = getHeight (); + + if (parentArea.intersectsWith (&childArea, intersection)) { + intersection->x -= allocation.x; + intersection->y -= allocation.y; + return true; + } else + return false; +} + +void Widget::setParent (Widget *parent) +{ + this->parent = parent; + layout = parent->layout; + + if (!buttonSensitiveSet) + buttonSensitive = parent->buttonSensitive; + + DBG_OBJ_ASSOC_PARENT (parent); + //printf ("The %s %p becomes a child of the %s %p\n", + // getClassName(), this, parent->getClassName(), parent); + + // Determine the container. Currently rather simple; will become + // more complicated when absolute and fixed positions are + // supported. + container = NULL; + for (Widget *widget = getParent (); widget != NULL && container == NULL; + widget = widget->getParent()) + if (widget->isPossibleContainer ()) + container = widget; + // If there is no possible container widget, there is + // (surprisingly!) also no container (i. e. the viewport is + // used). Does not occur in dillo, where the toplevel widget is a + // Textblock. + DBG_OBJ_SET_PTR ("container", container); + + notifySetParent(); +} + +void Widget::setQuasiParent (Widget *quasiParent) +{ + this->quasiParent = quasiParent; + + // More to do? Compare with setParent(). + + DBG_OBJ_SET_PTR ("quasiParent", quasiParent); +} + +void Widget::queueDrawArea (int x, int y, int width, int height) +{ + /** \todo Maybe only the intersection? */ + + DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d", + x, y, width, height); + + _MSG("Widget::queueDrawArea alloc(%d %d %d %d) wid(%d %d %d %d)\n", + allocation.x, allocation.y, + allocation.width, allocation.ascent + allocation.descent, + x, y, width, height); + if (layout) + layout->queueDraw (x + allocation.x, y + allocation.y, width, height); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief This method should be called, when a widget changes its size. + * + * A "fast" queueResize will ignore the anchestors, and furthermore + * not trigger the idle function. Used only within + * viewportSizeChanged, and not available outside Layout and Widget. + */ +void Widget::queueResize (int ref, bool extremesChanged, bool fast) +{ + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); + + // queueResize() can be called recursively; calls are queued, so + // that actualQueueResize() is clean. + + if (queueResizeEntered ()) { + DBG_OBJ_MSG ("resize", 1, "put into queue"); + layout->queueQueueResizeList->pushUnder (new Layout::QueueResizeItem + (this, ref, extremesChanged, + fast)); + } else { + actualQueueResize (ref, extremesChanged, fast); + + DBG_IF_RTFL { + if (layout == NULL) + DBG_OBJ_MSG ("resize", 1, "layout is not set"); + else if (layout->queueQueueResizeList->size () == 0) + DBG_OBJ_MSG ("resize", 1, "queue item list is empty"); + } + + while (layout != NULL && layout->queueQueueResizeList->size () > 0) { + DBG_IF_RTFL { + DBG_OBJ_MSGF ("resize", 1, "queue item list has %d elements:", + layout->queueQueueResizeList->size ()); +#if 0 + // TODO This worked when queueQueueResizeList was a Vector; now, + // iterators should be used. + DBG_OBJ_MSG_START (); + for (int i = 0; i < layout->queueQueueResizeList->size (); i++) { + DBG_OBJ_MSGF + ("resize", 1, + "#%d: widget = %p, ref = %d, extremesChanged = %s, " + "fast = %s", + i, layout->queueQueueResizeList->get(i)->widget, + layout->queueQueueResizeList->get(i)->ref, + layout->queueQueueResizeList->get(i)->extremesChanged ? + "true" : "false", + layout->queueQueueResizeList->get(i)->fast ? + "true" : "false"); + } + DBG_OBJ_MSG_END (); + DBG_OBJ_MSG ("resize", 1, "taking #0 out of list"); +#endif + } + + Layout::QueueResizeItem *item = + layout->queueQueueResizeList->getTop (); + item->widget->actualQueueResize (item->ref, item->extremesChanged, + item->fast); + layout->queueQueueResizeList->pop (); + } + } + + DBG_OBJ_LEAVE (); +} + +void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER ("resize", 0, "actualQueueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); + + enterQueueResize (); + + Widget *widget2, *child; + + Flags resizeFlag, extremesFlag; + + if (layout) { + // If RESIZE_QUEUED is set, this widget is already in the list. + if (!resizeQueued ()) + layout->queueResizeList->put (this); + + resizeFlag = RESIZE_QUEUED; + extremesFlag = EXTREMES_QUEUED; + } else { + resizeFlag = NEEDS_RESIZE; + extremesFlag = EXTREMES_CHANGED; + } + + setFlags (resizeFlag); + setFlags (ALLOCATE_QUEUED); + markSizeChange (ref); + + if (extremesChanged) { + setFlags (extremesFlag); + markExtremesChange (ref); + } + + if (fast) { + if (parent) { + // In this case, queueResize is called from top (may be a + // random entry point) to bottom, so markSizeChange and + // markExtremesChange have to be called explicitly for the + // parent. The tests (needsResize etc.) are uses to check + // whether queueResize has been called for the parent, or + // whether this widget is the enty point. + if (parent->needsResize () || parent->resizeQueued ()) + parent->markSizeChange (parentRef); + if (parent->extremesChanged () || parent->extremesQueued ()) + parent->markExtremesChange (parentRef); + } + } else { + for (widget2 = parent, child = this; widget2; + child = widget2, widget2 = widget2->parent) { + if (layout && !widget2->resizeQueued ()) + layout->queueResizeList->put (widget2); + + DBG_OBJ_MSGF ("resize", 2, "setting %s and ALLOCATE_QUEUED for %p", + resizeFlag == RESIZE_QUEUED ? + "RESIZE_QUEUED" : "NEEDS_RESIZE", + widget2); + + widget2->setFlags (resizeFlag); + widget2->markSizeChange (child->parentRef); + widget2->setFlags (ALLOCATE_QUEUED); + + if (extremesChanged) { + widget2->setFlags (extremesFlag); + widget2->markExtremesChange (child->parentRef); + } + } + + if (layout) + layout->queueResize (extremesChanged); + } + + leaveQueueResize (); + + DBG_OBJ_LEAVE (); +} + +void Widget::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + // If there is a container widget (not the viewport), which has not + // changed its size (which can be determined by the respective + // flags: this method is called recursively), this widget will + // neither change its size. Also, the recursive iteration can be + // stopped, since the children of this widget will + if (container == NULL || + container->needsResize () || container->resizeQueued () || + container->extremesChanged () || container->extremesQueued ()) { + // Viewport (container == NULL) or container widget has changed + // its size. + if (affectedByContainerSizeChange ()) + queueResizeFast (0, true); + + // Even if *this* widget is not affected, children may be, so + // iterate over children. + containerSizeChangedForChildren (); + } + + DBG_OBJ_LEAVE (); +} + +bool Widget::affectedByContainerSizeChange () +{ + DBG_OBJ_ENTER0 ("resize", 0, "affectedByContainerSizeChange"); + + bool ret; + + // This standard implementation is suitable for all widgets which + // call correctRequisition() and correctExtremes(), even in the way + // how Textblock and Image do (see comments there). Has to be kept + // in sync. + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width) && + style::isAbsLength (getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (getStyle()->width) || + style::isPerLength (getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (getStyle()->width == style::LENGTH_AUTO ? + usesAvailWidth () : false) || + (getStyle()->height == style::LENGTH_AUTO ? + usesAvailHeight () : false); + } else + ret = container->affectsSizeChangeContainerChild (this); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +bool Widget::affectsSizeChangeContainerChild (Widget *child) +{ + DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child); + + bool ret; + + // From the point of view of the container. This standard + // implementation should be suitable for most (if not all) + // containers. + + if (style::isAbsLength (child->getStyle()->width) && + style::isAbsLength (child->getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (child->getStyle()->width) || + style::isPerLength (child->getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (child->getStyle()->width == style::LENGTH_AUTO ? + child->usesAvailWidth () : false) || + (child->getStyle()->height == style::LENGTH_AUTO ? + child->usesAvailHeight () : false); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +void Widget::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + // Working, but inefficient standard implementation. + Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW | + Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + it->getContent()->widget->containerSizeChanged (); + it->unref (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailWidth() is called. + */ +bool Widget::usesAvailWidth () +{ + return false; +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailHeight() is called. + */ +bool Widget::usesAvailHeight () +{ + return false; +} + +/** + * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls + * the latter only when needed. + */ +void Widget::sizeRequest (Requisition *requisition) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + enterSizeRequest (); + + //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s, " + // "resizeQueued = %s\n", + // parent ? "non-" : "", getClassName(), this, parentRef, + // needsResize () ? "true" : "false", + // resizeQueued () ? "true" : "false"); + + if (resizeQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (NEEDS_RESIZE); + unsetFlags (RESIZE_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + + if (needsResize ()) { + /** \todo Check requisition == &(this->requisition) and do what? */ + sizeRequestImpl (requisition); + this->requisition = *requisition; + unsetFlags (NEEDS_RESIZE); + + DBG_OBJ_SET_NUM ("requisition.width", requisition->width); + DBG_OBJ_SET_NUM ("requisition.ascent", requisition->ascent); + DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent); + } else + *requisition = this->requisition; + + //printf (" ==> Result: %d x (%d + %d)\n", + // requisition->width, requisition->ascent, requisition->descent); + + leaveSizeRequest (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Used to evaluate Widget::adjustMinWidth. + * + * If extremes == NULL, getExtremes is called. ForceValue is the same + * value passed to getAvailWidth etc.; if false, getExtremes is not + * called. + */ +int Widget::getMinWidth (Extremes *extremes, bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "..., %s", + forceValue ? "true" : "false"); + int minWidth; + + if (getAdjustMinWidth ()) { + Extremes extremes2; + if (extremes == NULL) { + if (forceValue) { + getExtremes (&extremes2); + extremes = &extremes2; + } + } + + // TODO Not completely clear whether this is feasable: Within + // the context of getAvailWidth(false) etc., getExtremes may not + // be called. We ignore the minimal width then. + minWidth = extremes ? extremes->minWidthIntrinsic : 0; + } else + minWidth = 0; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", minWidth); + DBG_OBJ_LEAVE (); + + return minWidth; +} + +/** + * Return available width including margin/border/padding + * (extraSpace?), not only the content width. + */ +int Widget::getAvailWidth (bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "getAvailWidth", "%s", + forceValue ? "true" : "false"); + + int width; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + width = -1; + calcFinalWidth (getStyle (), viewportWidth, NULL, 0, forceValue, &width); + if (width == -1) + width = viewportWidth; + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + width = parent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + width = quasiParent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +/** + * Return available height including margin/border/padding + * (extraSpace?), not only the content height. + */ +int Widget::getAvailHeight (bool forceValue) +{ + // TODO Correct by ... not extremes, but ...? (Height extremes?) + + // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as + // "getAvailHeight (true)" is not used. + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeight", "%s", + forceValue ? "true" : "false"); + + int height; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + height = style::absLengthVal (getStyle()->height) + boxDiffHeight (); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); + // Notice that here -- unlike getAvailWidth() -- + // layout->hScrollbarThickness is not considered here; + // something like canvasWidthGreater (analogue to + // canvasHeightGreater) would be complicated and lead to + // possibly contradictory self-references. + height = applyPerHeight (layout->viewportHeight, getStyle()->height); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + height = layout->viewportHeight; + } + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + height = parent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int *, int *)) +{ + // TODO Correct height by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_ENTER ("resize", 0, "correctRequisition", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + int limitMinWidth = getMinWidth (NULL, true); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + calcFinalWidth (getStyle (), viewportWidth, NULL, limitMinWidth, false, + &requisition->width); + + // For layout->viewportHeight, see comment in getAvailHeight(). + int height = calcHeight (getStyle()->height, false, + layout->viewportHeight, NULL, false); + int minHeight = calcHeight (getStyle()->minHeight, false, + layout->viewportHeight, NULL, false); + int maxHeight = calcHeight (getStyle()->maxHeight, false, + layout->viewportHeight, NULL, false); + + // TODO Perhaps split first, then add box ascent and descent. + if (height != -1) + splitHeightFun (height, &requisition->ascent, &requisition->descent); + if (minHeight != -1 && + requisition->ascent + requisition->descent < minHeight) + splitHeightFun (minHeight, &requisition->ascent, + &requisition->descent); + if (maxHeight != -1 && + requisition->ascent + requisition->descent > maxHeight) + splitHeightFun (maxHeight, &requisition->ascent, + &requisition->descent); + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + parent->correctRequisitionOfChild (this, requisition, splitHeightFun); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + quasiParent->correctRequisitionOfChild (this, requisition, + splitHeightFun); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremes (Extremes *extremes) +{ + DBG_OBJ_ENTER ("resize", 0, "correctExtremes", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + if (container == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + int limitMinWidth = getMinWidth (extremes, false); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + + int width = calcWidth (getStyle()->width, viewportWidth, NULL, + limitMinWidth, false); + int minWidth = calcWidth (getStyle()->minWidth, viewportWidth, NULL, + limitMinWidth, false); + int maxWidth = calcWidth (getStyle()->maxWidth, viewportWidth, NULL, + limitMinWidth, false); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + extremes->minWidth = extremes->maxWidth = width; + if (minWidth != -1) + extremes->minWidth = minWidth; + if (maxWidth != -1) + extremes->maxWidth = maxWidth; + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + parent->correctExtremesOfChild (this, extremes); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + quasiParent->correctExtremesOfChild (this, extremes); + DBG_OBJ_MSG_END (); + } + + if (extremes->maxWidth < extremes->minWidth) + extremes->maxWidth = extremes->minWidth; + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +int Widget::calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "calcWidth", "0x%x, %d, %p, %d", + cssValue, refWidth, refWidget, limitMinWidth); + + assert (refWidth != -1 || refWidget != NULL); + + int width; + + if (style::isAbsLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (cssValue)); + width = misc::max (style::absLengthVal (cssValue) + boxDiffWidth (), + limitMinWidth); + } else if (style::isPerLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (cssValue)); + if (refWidth != -1) + width = misc::max (applyPerWidth (refWidth, cssValue), limitMinWidth); + else { + int availWidth = refWidget->getAvailWidth (forceValue); + if (availWidth != -1) { + int containerWidth = availWidth - refWidget->boxDiffWidth (); + width = misc::max (applyPerWidth (containerWidth, cssValue), + limitMinWidth); + } else + width = -1; + } + } else { + DBG_OBJ_MSG ("resize", 1, "not specified"); + width = -1; + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + return width; +} + +// *finalWidth may be -1. +void Widget::calcFinalWidth (style::Style *style, int refWidth, + Widget *refWidget, int limitMinWidth, + bool forceValue, int *finalWidth) +{ + DBG_OBJ_ENTER ("resize", 0, "calcFinalWidth", "..., %d, %p, %d, [%d]", + refWidth, refWidget, limitMinWidth, *finalWidth); + + int width = calcWidth (style->width, refWidth, refWidget, limitMinWidth, + forceValue); + int minWidth = calcWidth (style->minWidth, refWidth, refWidget, + limitMinWidth, forceValue); + int maxWidth = calcWidth (style->maxWidth, refWidth, refWidget, + limitMinWidth, forceValue); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + *finalWidth = width; + if (minWidth != -1 && *finalWidth != -1 && *finalWidth < minWidth) + *finalWidth = minWidth; + if (maxWidth != -1 && *finalWidth == -1 && *finalWidth > maxWidth) + *finalWidth = maxWidth; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", *finalWidth); + DBG_OBJ_LEAVE (); +} + +int Widget::calcHeight (style::Length cssValue, bool usePercentage, + int refHeight, Widget *refWidget, bool forceValue) +{ + // TODO Search for usage of this method and check the value of + // "usePercentage"; this has to be clarified. + + DBG_OBJ_ENTER ("resize", 0, "calcHeight", "0x%x, %s, %d, %p", + cssValue, usePercentage ? "true" : "false", refHeight, + refWidget); + + assert (refHeight != -1 || refWidget != NULL); + + int height; + + if (style::isAbsLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (cssValue)); + height = + misc::max (style::absLengthVal (cssValue) + boxDiffHeight (), 0); + } else if (style::isPerLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * + style::perLengthVal_useThisOnlyForDebugging (cssValue)); + if (usePercentage) { + if (refHeight != -1) + height = misc::max (applyPerHeight (refHeight, cssValue), 0); + else { + int availHeight = refWidget->getAvailHeight (forceValue); + if (availHeight != -1) { + int containerHeight = availHeight - refWidget->boxDiffHeight (); + height = + misc::max (applyPerHeight (containerHeight, cssValue), 0); + } else + height = -1; + } + } else + height = -1; + } else { + DBG_OBJ_MSG ("resize", 1, "not specified"); + height = -1; + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + return height; +} + +/** + * \brief Wrapper for Widget::getExtremesImpl(). + */ +void Widget::getExtremes (Extremes *extremes) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); + + enterGetExtremes (); + + if (extremesQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (EXTREMES_CHANGED); + unsetFlags (EXTREMES_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + + if (extremesChanged ()) { + // For backward compatibility (part 1/2): + extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; + + getExtremesImpl (extremes); + + // For backward compatibility (part 2/2): + if (extremes->minWidthIntrinsic == -1) + extremes->minWidthIntrinsic = extremes->minWidth; + if (extremes->maxWidthIntrinsic == -1) + extremes->maxWidthIntrinsic = extremes->maxWidth; + + this->extremes = *extremes; + unsetFlags (EXTREMES_CHANGED); + + DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth); + DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic", + extremes->minWidthIntrinsic); + DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); + DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic", + extremes->maxWidthIntrinsic); + } else + *extremes = this->extremes; + + leaveGetExtremes (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when + * needed. + */ +void Widget::sizeAllocate (Allocation *allocation) +{ + assert (!queueResizeEntered ()); + assert (!sizeRequestEntered ()); + assert (!getExtremesEntered ()); + assert (resizeIdleEntered ()); + + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + DBG_OBJ_MSGF ("resize", 1, + "old allocation (%d, %d; %d * (%d + %d)); needsAllocate: %s", + this->allocation.x, this->allocation.y, this->allocation.width, + this->allocation.ascent, this->allocation.descent, + needsAllocate () ? "true" : "false"); + + enterSizeAllocate (); + + /*printf ("The %stop-level %s %p is allocated:\n", + parent ? "non-" : "", getClassName(), this); + printf (" old = (%d, %d, %d + (%d + %d))\n", + this->allocation.x, this->allocation.y, this->allocation.width, + this->allocation.ascent, this->allocation.descent); + printf (" new = (%d, %d, %d + (%d + %d))\n", + allocation->x, allocation->y, allocation->width, allocation->ascent, + allocation->descent); + printf (" NEEDS_ALLOCATE = %s\n", needsAllocate () ? "true" : "false");*/ + + if (needsAllocate () || + allocation->x != this->allocation.x || + allocation->y != this->allocation.y || + allocation->width != this->allocation.width || + allocation->ascent != this->allocation.ascent || + allocation->descent != this->allocation.descent) { + + if (wasAllocated ()) { + layout->queueDrawExcept ( + this->allocation.x, + this->allocation.y, + this->allocation.width, + this->allocation.ascent + this->allocation.descent, + allocation->x, + allocation->y, + allocation->width, + allocation->ascent + allocation->descent); + } + + sizeAllocateImpl (allocation); + + //DEBUG_MSG (DEBUG_ALLOC, "... to %d, %d, %d x %d x %d\n", + // widget->allocation.x, widget->allocation.y, + // widget->allocation.width, widget->allocation.ascent, + // widget->allocation.descent); + + this->allocation = *allocation; + unsetFlags (NEEDS_ALLOCATE); + setFlags (WAS_ALLOCATED); + + resizeDrawImpl (); + + DBG_OBJ_SET_NUM ("allocation.x", this->allocation.x); + DBG_OBJ_SET_NUM ("allocation.y", this->allocation.y); + DBG_OBJ_SET_NUM ("allocation.width", this->allocation.width); + DBG_OBJ_SET_NUM ("allocation.ascent", this->allocation.ascent); + DBG_OBJ_SET_NUM ("allocation.descent", this->allocation.descent); + } + + /*unsetFlags (NEEDS_RESIZE);*/ + + leaveSizeAllocate (); + + DBG_OBJ_LEAVE (); +} + +bool Widget::buttonPress (EventButton *event) +{ + return buttonPressImpl (event); +} + +bool Widget::buttonRelease (EventButton *event) +{ + return buttonReleaseImpl (event); +} + +bool Widget::motionNotify (EventMotion *event) +{ + return motionNotifyImpl (event); +} + +void Widget::enterNotify (EventCrossing *event) +{ + enterNotifyImpl (event); +} + +void Widget::leaveNotify (EventCrossing *event) +{ + leaveNotifyImpl (event); +} + +/** + * \brief Change the style of a widget. + * + * The old style is automatically unreferred, the new is referred. If this + * call causes the widget to change its size, dw::core::Widget::queueResize + * is called. + */ +void Widget::setStyle (style::Style *style) +{ + bool sizeChanged; + + if (widgetImgRenderer && this->style && this->style->backgroundImage) + this->style->backgroundImage->removeExternalImgRenderer + (widgetImgRenderer); + + style->ref (); + + if (this->style) { + sizeChanged = this->style->sizeDiffs (style); + this->style->unref (); + } else + sizeChanged = true; + + this->style = style; + + DBG_OBJ_ASSOC_CHILD (style); + + if (style && style->backgroundImage) { + // Create instance of WidgetImgRenderer when needed. Until this + // widget is deleted, "widgetImgRenderer" will be kept, since it + // is not specific to the style, but only to this widget. + if (widgetImgRenderer == NULL) + widgetImgRenderer = new WidgetImgRenderer (this); + style->backgroundImage->putExternalImgRenderer (widgetImgRenderer); + } + + if (layout != NULL) { + layout->updateCursor (); + } + + if (sizeChanged) + queueResize (0, true); + else + queueDraw (); + + // These should better be attributed to the style itself, and a + // script processing RTFL messages could transfer it to something + // equivalent: + + DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top); + DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom); + DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left); + DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right); + + DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top); + DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); + DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left); + DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right); + + DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top); + DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom); + DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left); + DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right); + + DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing); + DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing); + + DBG_OBJ_SET_SYM ("style.display", + style->display == style::DISPLAY_BLOCK ? "block" : + style->display == style::DISPLAY_INLINE ? "inline" : + style->display == style::DISPLAY_INLINE_BLOCK ? + "inline-block" : + style->display == style::DISPLAY_LIST_ITEM ? "list-item" : + style->display == style::DISPLAY_NONE ? "none" : + style->display == style::DISPLAY_TABLE ? "table" : + style->display == style::DISPLAY_TABLE_ROW_GROUP ? + "table-row-group" : + style->display == style::DISPLAY_TABLE_HEADER_GROUP ? + "table-header-group" : + style->display == style::DISPLAY_TABLE_FOOTER_GROUP ? + "table-footer-group" : + style->display == style::DISPLAY_TABLE_ROW ? "table-row" : + style->display == style::DISPLAY_TABLE_CELL ? "table-cell" : + "???"); + + DBG_OBJ_SET_NUM ("style.width (raw)", style->width); + DBG_OBJ_SET_NUM ("style.min-width (raw)", style->minWidth); + DBG_OBJ_SET_NUM ("style.max-width (raw)", style->maxWidth); + DBG_OBJ_SET_NUM ("style.height (raw)", style->height); + DBG_OBJ_SET_NUM ("style.min-height (raw)", style->minHeight); + DBG_OBJ_SET_NUM ("style.max-height (raw)", style->maxHeight); +} + +/** + * \brief Set the background "behind" the widget, if it is not the + * background of the parent widget, e.g. the background of a table + * row. + */ +void Widget::setBgColor (style::Color *bgColor) +{ + this->bgColor = bgColor; +} + +/** + * \brief Get the actual background of a widget. + */ +style::Color *Widget::getBgColor () +{ + Widget *widget = this; + + while (widget != NULL) { + if (widget->style->backgroundColor) + return widget->style->backgroundColor; + if (widget->bgColor) + return widget->bgColor; + + widget = widget->parent; + } + + return layout->getBgColor (); +} + + +/** + * \brief Draw borders and background of a widget part, which allocation is + * given by (x, y, width, height) (widget coordinates). + * + * area is given in widget coordinates. + */ +void Widget::drawBox (View *view, style::Style *style, Rectangle *area, + int x, int y, int width, int height, bool inverse) +{ + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; + + style::drawBorder (view, layout, &canvasArea, + allocation.x + x, allocation.y + y, + width, height, style, inverse); + + // This method is used for inline elements, where the CSS 2 specification + // does not define what here is called "reference area". To make it look + // smoothly, the widget padding box is used. + + // TODO Handle inverse drawing the same way as in drawWidgetBox? + // Maybe this method (drawBox) is anyway obsolete when extraSpace + // is fully supported (as in the "dillo_grows" repository). + + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + style::drawBackground + (view, layout, &canvasArea, + allocation.x + x + style->margin.left + style->borderWidth.left, + allocation.y + y + style->margin.top + style->borderWidth.top, + width - style->margin.left - style->borderWidth.left + - style->margin.right - style->borderWidth.right, + height - style->margin.top - style->borderWidth.top + - style->margin.bottom - style->borderWidth.bottom, + xPad, yPad, widthPad, heightPad, style, style->backgroundColor, + inverse, false); +} + +/** + * \brief Draw borders and background of a widget. + * + * area is given in widget coordinates. + * + */ +void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) +{ + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; + + style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y, + allocation.width, getHeight (), style, inverse); + + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + + style::Color *bgColor; + if (inverse && style->backgroundColor == NULL) { + // See style::drawBackground: for inverse drawing, we need a + // defined background color. Search through ancestors. + Widget *w = this; + while (w != NULL && w->style->backgroundColor == NULL) + w = w->parent; + + if (w != NULL && w->style->backgroundColor != NULL) + bgColor = w->style->backgroundColor; + else + bgColor = layout->getBgColor (); + } else + bgColor = style->backgroundColor; + + style::drawBackground (view, layout, &canvasArea, + xPad, yPad, widthPad, heightPad, + xPad, yPad, widthPad, heightPad, + style, bgColor, inverse, parent == NULL); +} + +/* + * This function is used by some widgets, when they are selected (as a whole). + * + * \todo This could be accelerated by using clipping bitmaps. Two important + * issues: + * + * (i) There should always been a pixel in the upper-left corner of the + * *widget*, so probably two different clipping bitmaps have to be + * used (10/01 and 01/10). + * + * (ii) Should a new GC always be created? + * + * \bug Not implemented. + */ +void Widget::drawSelected (View *view, Rectangle *area) +{ +} + + +void Widget::setButtonSensitive (bool buttonSensitive) +{ + this->buttonSensitive = buttonSensitive; + buttonSensitiveSet = true; +} + + +/** + * \brief Get the widget at the root of the tree, this widget is part from. + */ +Widget *Widget::getTopLevel () +{ + Widget *widget = this; + + while (widget->parent) + widget = widget->parent; + + return widget; +} + +/** + * \brief Get the level of the widget within the tree. + * + * The root widget has the level 0. + */ +int Widget::getLevel () +{ + Widget *widget = this; + int level = 0; + + while (widget->parent) { + level++; + widget = widget->parent; + } + + return level; +} + +/** + * \brief Get the level of the widget within the tree, regarting the + * generators, not the parents. + * + * The root widget has the level 0. + */ +int Widget::getGeneratorLevel () +{ + Widget *widget = this; + int level = 0; + + while (widget->getGenerator ()) { + level++; + widget = widget->getGenerator (); + } + + return level; +} + +/** + * \brief Get the widget with the highest level, which is a direct ancestor of + * widget1 and widget2. + */ +Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) +{ + Widget *widget1 = this, *widget2 = otherWidget; + int level1 = widget1->getLevel (), level2 = widget2->getLevel(); + + /* Get both widgets onto the same level.*/ + while (level1 > level2) { + widget1 = widget1->parent; + level1--; + } + + while (level2 > level1) { + widget2 = widget2->parent; + level2--; + } + + /* Search upwards. */ + while (widget1 != widget2) { + if (widget1->parent == NULL) { + MSG_WARN("widgets in different trees\n"); + return NULL; + } + + widget1 = widget1->parent; + widget2 = widget2->parent; + } + + return widget1; +} + + +/** + * \brief Search recursively through widget. + * + * Used by dw::core::Layout:getWidgetAtPoint. + */ +Widget *Widget::getWidgetAtPoint (int x, int y, int level) +{ + Iterator *it; + Widget *childAtPoint; + + //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", + // 3 * level, "", getClassName (), this, allocation.x, allocation.y, + // allocation.width, allocation.ascent, allocation.descent); + + if (x >= allocation.x && + y >= allocation.y && + x <= allocation.x + allocation.width && + y <= allocation.y + getHeight ()) { + //_MSG ("%*s -> inside\n", 3 * level, ""); + /* + * Iterate over the children of this widget. Test recursively, whether + * the point is within the child (or one of its children...). If there + * is such a child, it is returned. Otherwise, this widget is returned. + */ + childAtPoint = NULL; + it = iterator ((Content::Type) + (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), + false); + + while (childAtPoint == NULL && it->next ()) { + Widget *child = it->getContent()->widget; + if (child->wasAllocated ()) + childAtPoint = child->getWidgetAtPoint (x, y, level + 1); + } + + it->unref (); + + if (childAtPoint) + return childAtPoint; + else + return this; + } else + return NULL; +} + + +void Widget::scrollTo (HPosition hpos, VPosition vpos) +{ + layout->scrollToWidget (hpos, vpos, this); +} + + +void Widget::scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height) +{ + // TODO This should perhaps better be done by a new Layout::ScrollTarget. + layout->scrollTo (hpos, vpos, + x + allocation.x, y + allocation.y, width, height); +} + + +/** + * \brief Return the padding area (content plus padding). + * + * Used as "reference area" (ee comment of "style::drawBackground") + * for backgrounds. + */ +void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, + int *heightPad) +{ + *xPad = allocation.x + style->margin.left + style->borderWidth.left; + *yPad = allocation.y + style->margin.top + style->borderWidth.top; + *widthPad = allocation.width - style->margin.left - style->borderWidth.left + - style->margin.right - style->borderWidth.right; + *heightPad = getHeight () - style->margin.top - style->borderWidth.top + - style->margin.bottom - style->borderWidth.bottom; +} + +void Widget::sizeAllocateImpl (Allocation *allocation) +{ +} + +void Widget::markSizeChange (int ref) +{ +} + +void Widget::markExtremesChange (int ref) +{ +} + +int Widget::applyPerWidth (int containerWidth, style::Length perWidth) +{ + return style::multiplyWithPerLength (containerWidth, perWidth) + + boxDiffWidth (); +} + +int Widget::applyPerHeight (int containerHeight, style::Length perHeight) +{ + return style::multiplyWithPerLength (containerHeight, perHeight) + + boxDiffHeight (); +} + +int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // This is a halfway suitable implementation for all + // containers. For simplification, this will be used during the + // development; then, a differentiation could be possible. + + DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + width = misc::max (getAvailWidth (true) - boxDiffWidth (), 0); + else + width = -1; + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case, regard also parent. And quasiParent. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + width = -1; + child->calcFinalWidth (child->getStyle(), -1, this, 0, forceValue, + &width); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + width = effContainer->getAvailWidthOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int Widget::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as + // "getAvailHeight (true)" is not used. + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + height = misc::max (getAvailHeight (true) - boxDiffHeight (), 0); + else + height = -1; + } else { + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (child->getStyle()->height)); + height = misc::max (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), 0); + } else { + assert (style::isPerLength (child->getStyle()->height)); + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->height)); + + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = + misc::max (child->applyPerHeight (availHeight - + boxDiffHeight (), + child->getStyle()->height), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + height = effContainer->getAvailHeightOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Again, a suitable implementation for all widgets (perhaps). + + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + correctReqWidthOfChild (child, requisition); + correctReqHeightOfChild (child, requisition, splitHeightFun); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + effContainer->correctRequisitionOfChild (child, requisition, + splitHeightFun); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition) +{ + DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild", "%p, %d * (%d + %d)", + child, requisition->width, requisition->ascent, + requisition->descent); + + assert (this == child->quasiParent || this == child->container); + + int limitMinWidth = child->getMinWidth (NULL, true); + child->calcFinalWidth (child->getStyle(), -1, this, limitMinWidth, false, + &requisition->width); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + // TODO Correct height by extremes? (Height extemes?) + + assert (this == child->quasiParent || this == child->container); + + DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + int height = child->calcHeight (child->getStyle()->height, false, -1, this, + false); + int minHeight = child->calcHeight (child->getStyle()->minHeight, false, -1, + this, false); + int maxHeight = child->calcHeight (child->getStyle()->maxHeight, false, -1, + this, false); + + // TODO Perhaps split first, then add box ascent and descent. + if (height != -1) + splitHeightFun (height, &requisition->ascent, &requisition->descent); + if (minHeight != -1 && + requisition->ascent + requisition->descent < minHeight) + splitHeightFun (minHeight, &requisition->ascent, + &requisition->descent); + if (maxHeight != -1 && + requisition->ascent + requisition->descent > maxHeight) + splitHeightFun (maxHeight, &requisition->ascent, + &requisition->descent); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // See comment in correctRequisitionOfChild. + + DBG_OBJ_ENTER ("resize", 0, "correctExtremesOfChild", + "%p, %d (%d) / %d (%d)", + child, extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + int limitMinWidth = child->getMinWidth (extremes, false); + int width = child->calcWidth (child->getStyle()->width, -1, this, + limitMinWidth, false); + int minWidth = child->calcWidth (child->getStyle()->minWidth, -1, this, + limitMinWidth, false); + int maxWidth = child->calcWidth (child->getStyle()->maxWidth, -1, this, + limitMinWidth, false); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + extremes->minWidth = extremes->maxWidth = width; + if (minWidth != -1) + extremes->minWidth = minWidth; + if (maxWidth != -1) + extremes->maxWidth = maxWidth; + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + effContainer->correctExtremesOfChild (child, extremes); + DBG_OBJ_MSG_END (); + } + + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +/** + * \brief This method is called after a widget has been set as the top of a + * widget tree. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetAsTopLevel() +{ +} + +/** + * \brief This method is called after a widget has been added to a parent. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetParent() +{ +} + +bool Widget::isBlockLevel () +{ + // Most widgets are not block-level. + return false; +} + +bool Widget::isPossibleContainer () +{ + // In most (all?) cases identical to: + return isBlockLevel (); +} + +bool Widget::buttonPressImpl (EventButton *event) +{ + return false; +} + +bool Widget::buttonReleaseImpl (EventButton *event) +{ + return false; +} + +bool Widget::motionNotifyImpl (EventMotion *event) +{ + return false; +} + +void Widget::enterNotifyImpl (EventCrossing *) +{ + style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onEnter(); +} + +void Widget::leaveNotifyImpl (EventCrossing *) +{ + style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onLeave(); +} + +void Widget::removeChild (Widget *child) +{ + // Should be implemented. + misc::assertNotReached (); +} + +// ---------------------------------------------------------------------- + +void splitHeightPreserveAscent (int height, int *ascent, int *descent) +{ + *descent = height - *ascent; + if (*descent < 0) { + *descent = 0; + *ascent = height; + } +} + +void splitHeightPreserveDescent (int height, int *ascent, int *descent) +{ + *ascent = height - *descent; + if (*ascent < 0) { + *ascent = 0; + *descent = height; + } +} + +} // namespace core +} // namespace dw diff --git a/dw/widget.hh b/dw/widget.hh new file mode 100644 index 0000000..d107fe1 --- /dev/null +++ b/dw/widget.hh @@ -0,0 +1,514 @@ +#ifndef __DW_WIDGET_HH__ +#define __DW_WIDGET_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/identity.hh" + +/** + * \brief The type for callback functions. + */ +typedef void (*DW_Callback_t)(void *data); + +namespace dw { +namespace core { + +/** + * \brief The base class of all dillo widgets. + * + * \sa\ref dw-overview, \ref dw-layout-widgets + */ +class Widget: public lout::identity::IdentifiableObject +{ + friend class Layout; + +protected: + enum Flags { + /** + * \todo Comment this. + */ + RESIZE_QUEUED = 1 << 0, + + /** + * \todo Comment this. + */ + EXTREMES_QUEUED = 1 << 1, + + /** + * \brief Set, when dw::core::Widget::requisition is not up to date + * anymore. + * + * \todo Update, see RESIZE_QUEUED. + */ + NEEDS_RESIZE = 1 << 2, + + /** + * \brief Only used internally, set to enforce size allocation. + * + * In some cases, the size of a widget remains the same, but the + * children are allocated at different positions and in + * different sizes, so that a simple comparison of old and new + * allocation is insufficient. Therefore, this flag is set + * (indirectly, as ALLOCATE_QUEUED) in queueResize. + */ + NEEDS_ALLOCATE = 1 << 3, + + /** + * \todo Comment this. + */ + ALLOCATE_QUEUED = 1 << 4, + + /** + * \brief Set, when dw::core::Widget::extremes is not up to date + * anymore. + * + * \todo Update, see RESIZE_QUEUED. + */ + EXTREMES_CHANGED = 1 << 5, + + /** + * \brief Set, when a widget was already once allocated, + * + * The dw::Image widget uses this flag, see dw::Image::setBuffer. + */ + WAS_ALLOCATED = 1 << 6, + }; + + /** + * \brief Implementation which represents the whole widget. + * + * The only instance is set created needed. + */ + class WidgetImgRenderer: public style::StyleImage::ExternalWidgetImgRenderer + { + private: + Widget *widget; + + public: + inline WidgetImgRenderer (Widget *widget) { this->widget = widget; } + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + style::Style *getStyle (); + void draw (int x, int y, int width, int height); + }; + + WidgetImgRenderer *widgetImgRenderer; + +private: + static bool adjustMinWidth; + + /** + * \brief The parent widget, NULL for top-level widgets. + */ + Widget *parent; + + /** + * \brief ... + */ + Widget *quasiParent; + + /** + * \brief The generating widget, NULL for top-level widgets, or if + * not set; in the latter case, the effective generator (see + * getGenerator) is the parent. + */ + Widget *generator; + + /** + * \brief The containing widget, equivalent to the "containing + * block" defined by CSS. May be NULL, in this case the viewport + * is used. + */ + Widget *container; + + style::Style *style; + + Flags flags; + + /** + * \brief Size_request() stores the result of the last call of + * size_request_impl(). + * + * Do not read this directly, but call size_request(). + */ + Requisition requisition; + + /** + * \brief Analogue to dw::core::Widget::requisition. + */ + Extremes extremes; + + /** + * \brief See dw::core::Widget::setBgColor(). + */ + style::Color *bgColor; + + /** + * \brief See dw::core::Widget::setButtonSensitive(). + */ + bool buttonSensitive; + + /** + * \brief See dw::core::Widget::setButtonSensitive(). + */ + bool buttonSensitiveSet; + + void queueResize (int ref, bool extremesChanged, bool fast); + inline void queueResizeFast (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, true); } + void actualQueueResize (int ref, bool extremesChanged, bool fast); + +public: + /** + * \brief This value is defined by the parent widget, and used for + * incremential resizing. + * + * See documentation for an explanation. + */ + int parentRef; + +protected: + + /** + * \brief The current allocation: size and position, always relative to the + * canvas. + */ + Allocation allocation; + + inline int getHeight () { return allocation.ascent + allocation.descent; } + inline int getContentWidth() { return allocation.width + - style->boxDiffWidth (); } + inline int getContentHeight() { return getHeight () + - style->boxDiffHeight (); } + + Layout *layout; + + /** + * \brief Space around the margin box. Allocation is extraSpace + + * margin + border + padding + contents; + */ + style::Box extraSpace; + + /*inline void printFlags () { + DBG_IF_RTFL { + char buf[10 * 3 - 1 + 1]; + snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s", + (flags & RESIZE_QUEUED) ? "Rq" : "--", + (flags & EXTREMES_QUEUED) ? "Eq" : "--", + (flags & NEEDS_RESIZE) ? "nR" : "--", + (flags & NEEDS_ALLOCATE) ? "nA" : "--", + (flags & ALLOCATE_QUEUED) ? "Aq" : "--", + (flags & EXTREMES_CHANGED) ? "Ec" : "--", + (flags & WAS_ALLOCATED) ? "wA" : "--"); + DBG_OBJ_SET_SYM ("flags", buf); + } + }*/ + + inline void printFlag (Flags f) { + DBG_IF_RTFL { + switch (f) { + case RESIZE_QUEUED: + DBG_OBJ_SET_SYM ("flags.RESIZE_QUEUED", + (flags & RESIZE_QUEUED) ? "true" : "false"); + break; + + case EXTREMES_QUEUED: + DBG_OBJ_SET_SYM ("flags.EXTREMES_QUEUED", + (flags & EXTREMES_QUEUED) ? "true" : "false"); + break; + + case NEEDS_RESIZE: + DBG_OBJ_SET_SYM ("flags.NEEDS_RESIZE", + (flags & NEEDS_RESIZE) ? "true" : "false"); + break; + + case NEEDS_ALLOCATE: + DBG_OBJ_SET_SYM ("flags.NEEDS_ALLOCATE", + (flags & NEEDS_ALLOCATE) ? "true" : "false"); + break; + + case ALLOCATE_QUEUED: + DBG_OBJ_SET_SYM ("flags.ALLOCATE_QUEUED", + (flags & ALLOCATE_QUEUED) ? "true" : "false"); + break; + + case EXTREMES_CHANGED: + DBG_OBJ_SET_SYM ("flags.EXTREMES_CHANGED", + (flags & EXTREMES_CHANGED) ? "true" : "false"); + break; + + case WAS_ALLOCATED: + DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED", + (flags & WAS_ALLOCATED) ? "true" : "false"); + break; + } + } + } + + inline void setFlags (Flags f) + { flags = (Flags)(flags | f); printFlag (f); } + inline void unsetFlags (Flags f) + { flags = (Flags)(flags & ~f); printFlag (f); } + + + inline void queueDraw () + { queueDrawArea (0, 0, allocation.width, getHeight()); } + void queueDrawArea (int x, int y, int width, int height); + inline void queueResize (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, false); } + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void sizeRequestImpl (Requisition *requisition) = 0; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void getExtremesImpl (Extremes *extremes) = 0; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void sizeAllocateImpl (Allocation *allocation); + + /** + * \brief Called after sizeAllocateImpl() to redraw necessary areas. + * By default the whole widget is redrawn. + */ + virtual void resizeDrawImpl () { queueDraw (); }; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void markSizeChange (int ref); + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void markExtremesChange (int ref); + + int getMinWidth (Extremes *extremes, bool forceValue); + + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + void correctReqWidthOfChild (Widget *child, Requisition *requisition); + void correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + + virtual void containerSizeChangedForChildren (); + + virtual bool affectedByContainerSizeChange (); + virtual bool affectsSizeChangeContainerChild (Widget *child); + virtual bool usesAvailWidth (); + virtual bool usesAvailHeight (); + + virtual void notifySetAsTopLevel(); + virtual void notifySetParent(); + + virtual bool buttonPressImpl (EventButton *event); + virtual bool buttonReleaseImpl (EventButton *event); + virtual bool motionNotifyImpl (EventMotion *event); + virtual void enterNotifyImpl (EventCrossing *event); + virtual void leaveNotifyImpl (EventCrossing *event); + + inline char *addAnchor (const char* name) + { return layout->addAnchor (this, name); } + + inline char *addAnchor (const char* name, int y) + { return layout->addAnchor (this, name, y); } + + inline void changeAnchor (char* name, int y) + { layout->changeAnchor (this, name, y); } + + inline void removeAnchor (char* name) + { if (layout) layout->removeAnchor (this, name); } + + //inline void updateBgColor () { layout->updateBgColor (); } + + inline void setCursor (style::Cursor cursor) + { layout->setCursor (cursor); } +#if 0 + inline bool selectionButtonPress (Iterator *it, int charPos, int linkNo, + EventButton *event, bool withinContent) + { return layout->selectionState.buttonPress (it, charPos, linkNo, event); } + + inline bool selectionButtonRelease (Iterator *it, int charPos, int linkNo, + EventButton *event, bool withinContent) + { return layout->selectionState.buttonRelease (it, charPos, linkNo, event);} + + inline bool selectionButtonMotion (Iterator *it, int charPos, int linkNo, + EventMotion *event, bool withinContent) + { return layout->selectionState.buttonMotion (it, charPos, linkNo, event); } +#endif + inline bool selectionHandleEvent (SelectionState::EventType eventType, + Iterator *it, int charPos, int linkNo, + MousePositionEvent *event) + { return layout->selectionState.handleEvent (eventType, it, charPos, linkNo, + event); } + +private: + void *deleteCallbackData; + DW_Callback_t deleteCallbackFunc; + +public: + inline void setDeleteCallback(DW_Callback_t func, void *data) + { deleteCallbackFunc = func; deleteCallbackData = data; } + +private: + bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; } + + void enterQueueResize () { if (layout) layout->queueResizeCounter++; } + void leaveQueueResize () { if (layout) layout->queueResizeCounter--; } + bool queueResizeEntered () { return layout && layout->queueResizeCounter; } + + void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; } + void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; } + bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; } + + void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; } + void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; } + bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; } + + void enterGetExtremes () { if (layout) layout->getExtremesCounter++; } + void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; } + bool getExtremesEntered () { return layout && layout->getExtremesCounter; } + + +public: + static int CLASS_ID; + + inline static void setAdjustMinWidth (bool adjustMinWidth) + { Widget::adjustMinWidth = adjustMinWidth; } + + Widget (); + ~Widget (); + + inline bool resizeQueued () { return flags & RESIZE_QUEUED; } + inline bool extremesQueued () { return flags & EXTREMES_QUEUED; } + inline bool needsResize () { return flags & NEEDS_RESIZE; } + inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; } + inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; } + inline bool extremesChanged () { return flags & EXTREMES_CHANGED; } + inline bool wasAllocated () { return flags & WAS_ALLOCATED; } + + void setParent (Widget *parent); + void setQuasiParent (Widget *quasiParent); + + void setGenerator (Widget *generator) { this->generator = generator; } + + inline style::Style *getStyle () { return style; } + /** \todo I do not like this. */ + inline Allocation *getAllocation () { return &allocation; } + + inline int boxOffsetX () + { return extraSpace.left + getStyle()->boxOffsetX (); } + inline int boxRestWidth () + { return extraSpace.right + getStyle()->boxRestWidth (); } + inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } + inline int boxOffsetY () + { return extraSpace.top + getStyle()->boxOffsetY (); } + inline int boxRestHeight () + { return extraSpace.bottom + getStyle()->boxRestHeight (); } + inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + + void sizeRequest (Requisition *requisition); + void getExtremes (Extremes *extremes); + void sizeAllocate (Allocation *allocation); + + int getAvailWidth (bool forceValue); + int getAvailHeight (bool forceValue); + virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; } + void correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremes (Extremes *extremes); + int calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue); + void calcFinalWidth (style::Style *style, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue, int *finalWidth); + int calcHeight (style::Length cssValue, bool usePercentage, int refHeight, + Widget *refWidget, bool forceValue); + + virtual int applyPerWidth (int containerWidth, style::Length perWidth); + virtual int applyPerHeight (int containerHeight, style::Length perHeight); + + virtual bool isBlockLevel (); + virtual bool isPossibleContainer (); + + void containerSizeChanged (); + + bool intersects (Rectangle *area, Rectangle *intersection); + + /** Area is given in widget coordinates. */ + virtual void draw (View *view, Rectangle *area) = 0; + + bool buttonPress (EventButton *event); + bool buttonRelease (EventButton *event); + bool motionNotify (EventMotion *event); + void enterNotify (EventCrossing *event); + void leaveNotify (EventCrossing *event); + + virtual void setStyle (style::Style *style); + void setBgColor (style::Color *bgColor); + style::Color *getBgColor (); + + void drawBox (View *view, style::Style *style, Rectangle *area, + int x, int y, int width, int height, bool inverse); + void drawWidgetBox (View *view, Rectangle *area, bool inverse); + void drawSelected (View *view, Rectangle *area); + + void setButtonSensitive (bool buttonSensitive); + inline bool isButtonSensitive () { return buttonSensitive; } + + inline Widget *getParent () { return parent; } + inline Widget *getContainer () { return container; } + Widget *getTopLevel (); + int getLevel (); + int getGeneratorLevel (); + Widget *getNearestCommonAncestor (Widget *otherWidget); + + inline Widget *getGenerator () { return generator ? generator : parent; } + + inline Layout *getLayout () { return layout; } + + virtual Widget *getWidgetAtPoint (int x, int y, int level); + + void scrollTo (HPosition hpos, VPosition vpos); + void scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height); + + void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad); + + /** + * \brief Return an iterator for this widget. + * + * \em mask can narrow the types returned by the iterator, this can + * enhance performance quite much, e.g. when only searching for child + * widgets. + * + * With \em atEnd == false, the iterator starts \em before the beginning, + * i.e. the first call of dw::core::Iterator::next will let the iterator + * point on the first piece of contents. Likewise, With \em atEnd == true, + * the iterator starts \em after the last piece of contents, call + * dw::core::Iterator::prev in this case. + */ + virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void removeChild (Widget *child); +}; + +void splitHeightPreserveAscent (int height, int *ascent, int *descent); +void splitHeightPreserveDescent (int height, int *ascent, int *descent); + +} // namespace core +} // namespace dw + +#endif // __DW_WIDGET_HH__ diff --git a/dwr/Makefile.am b/dwr/Makefile.am new file mode 100644 index 0000000..afd9925 --- /dev/null +++ b/dwr/Makefile.am @@ -0,0 +1,31 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dwr"' + +noinst_LIBRARIES = \ + libDw-rtfl.a + +libDw_rtfl_a_SOURCES = \ + box.hh \ + box.cc \ + graph.hh \ + graph.cc \ + hbox.hh \ + hbox.cc \ + hideable.hh \ + hideable.cc \ + label.hh \ + label.cc \ + toggle.hh \ + toggle.cc \ + tools.hh \ + tools.cc \ + vbox.hh \ + vbox.cc + +if USE_GRAPH2 +libDw_rtfl_a_SOURCES += \ + graph2.hh \ + graph2.cc \ + graph2_iterator.cc +endif diff --git a/dwr/box.cc b/dwr/box.cc new file mode 100644 index 0000000..2e72866 --- /dev/null +++ b/dwr/box.cc @@ -0,0 +1,258 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "box.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::container::typed; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Box::CLASS_ID = -1; + +#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + +const char Box::countZeroTable[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + LT(3), LT(2), LT(2), LT(1), LT(1), LT(1), LT(1), + LT(0), LT(0), LT(0), LT(0), LT(0), LT(0), LT(0), LT(0) +}; + +// ---------------------------------------------------------------------- + +Box::BoxIterator::BoxIterator (Box *box, Content::Type mask, + bool atEnd) : Iterator (box, mask, atEnd) +{ + index = atEnd ? box->children->size() : -1; + content.type = atEnd ? Content::END : Content::START; +} + +Box::BoxIterator::BoxIterator (Box *box, Content::Type mask, + int index) : Iterator (box, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = Content::START; + else if (index >= box->children->size ()) + content.type = Content::END; + else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = box->children->get (index); + } +} + +lout::object::Object *Box::BoxIterator::clone () +{ + return new BoxIterator ((Box*)getWidget(), getMask(), index); +} + +int Box::BoxIterator::compareTo (lout::object::Comparable *other) +{ + return index - ((BoxIterator*)other)->index; +} + +bool Box::BoxIterator::next () +{ + Box *box = (Box*)getWidget(); + + if (content.type == Content::END) + return false; + + // boxs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::END; + return false; + } + + index++; + if (index >= box->children->size ()) { + content.type = Content::END; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = box->children->get (index); + return true; + } +} + +bool Box::BoxIterator::prev () +{ + Box *box = (Box*)getWidget(); + + if (content.type == Content::START) + return false; + + // boxs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::START; + return false; + } + + index--; + if (index < 0) { + content.type = Content::START; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = box->children->get (index); + return true; + } +} + +void Box::BoxIterator::highlight (int start, int end, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Box::BoxIterator::unhighlight (int direction, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Box::BoxIterator::getAllocation (int start, int end, + Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +// ---------------------------------------------------------------------- + +Box::Box (bool stretchChildren) +{ + DBG_OBJ_CREATE ("rtfl::dw::Box"); + registerName ("rtfl::dw::Box", &CLASS_ID); + + this->stretchChildren = stretchChildren; + children = new Vector<Widget> (4, true); + inDestructor = false; +} + +Box::~Box () +{ + inDestructor = true; + delete children; + + DBG_OBJ_DELETE (); +} + +void Box::sizeRequestImplImpl (Requisition *requisition) +{ + actualSizeRequestImplImpl (requisition, 0); +} + +void Box::actualSizeRequestImplImpl (::dw::core::Requisition *requisition, + int data1) +{ + // "data1" is a value passed from a special implementation of + // sizeRequestImplImpl to accumulateSize. See VBox for an example + // on usage. Extend by "data2" ... when needed. + + requisition->width = requisition->ascent = requisition->descent = 0; + + for (int i = 0; i < children->size (); i++) { + Requisition childReq; + children->get(i)->sizeRequest (&childReq); + accumulateSize (i, children->size (), requisition, &childReq, data1); + } + + requisition->width += getStyle()->boxDiffWidth (); + requisition->ascent += getStyle()->boxOffsetY (); + requisition->descent += getStyle()->boxRestHeight (); +} + +void Box::getExtremesImplImpl (Extremes *extremes) +{ + extremes->minWidth = extremes->maxWidth = 0; + + for (int i = 0; i < children->size (); i++) { + Extremes childExtr; + children->get(i)->getExtremes (&childExtr); + accumulateExtremes (i, children->size (), extremes, &childExtr); + } + + extremes->minWidth += getStyle()->boxDiffWidth (); + extremes->maxWidth += getStyle()->boxDiffWidth (); +} + +void Box::drawImpl (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawWidgetBox (view, area, false); + for (int i = 0; i < children->size (); i++) { + Widget *child = children->get (i); + Rectangle childArea; + if (child->intersects (area, &childArea)) + child->draw (view, &childArea); + } + + DBG_OBJ_LEAVE (); +} + +::dw::core::Iterator *Box::iterator (Content::Type mask, bool atEnd) +{ + return new BoxIterator (this, mask, atEnd); +} + +void Box::removeChild (Widget *child) +{ + if (!inDestructor) { + for (int i = 0; i < children->size (); i++) { + if (children->get (i) == child) { + children->remove (i); + return; + } + } + + assertNotReached (); + } +} + +void Box::addChild (Widget *widget, int newPos) +{ + if (newPos == -1) + children->put (widget); + else + children->insert (widget, newPos); + + widget->setParent (this); + queueResize (0, true); +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/box.hh b/dwr/box.hh new file mode 100644 index 0000000..2fe5b2b --- /dev/null +++ b/dwr/box.hh @@ -0,0 +1,110 @@ +#ifndef __DWR_BOX_HH__ +#define __DWR_BOX_HH__ + +#include "hideable.hh" + +namespace rtfl { + +namespace dw { + +class Box: public Hideable +{ +private: + class BoxIterator: public ::dw::core::Iterator + { + private: + int index; + + BoxIterator (Box *box, ::dw::core::Content::Type mask, int index); + + public: + BoxIterator (Box *box, ::dw::core::Content::Type mask, bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, ::dw::core::HighlightLayer layer); + void unhighlight (int direction, ::dw::core::HighlightLayer layer); + void getAllocation (int start, int end, + ::dw::core::Allocation *allocation); + }; + + bool inDestructor; + static const char countZeroTable[256]; + + /** + * \brief Calculate the number of bits 0 on the left. + * + * This is similar to calculating the logarithm base 2 (which finds + * the right-most bit 1), and a lookup as described at + * <http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup> + * is used. + */ + static inline int getZeroBits (int i) + { + // TODO This should not work for 64 bit integers. + unsigned int ih, ilh, ihh; + if ((ih = i >> 16)) + return ((ihh = ih >> 8)) ? + countZeroTable[ihh] : 8 + countZeroTable[ih]; + else + return ((ilh = i >> 8)) ? + 16 + countZeroTable[ilh] : 24 + countZeroTable[i]; + } + +protected: + lout::container::typed::Vector<Widget> *children; + bool stretchChildren; + + void sizeRequestImplImpl (::dw::core::Requisition *requisition); + void actualSizeRequestImplImpl (::dw::core::Requisition *requisition, + int data1); + void getExtremesImplImpl (::dw::core::Extremes *extremes); + + void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area); + + /** + * \brief Return a * b / c, but avoid overflow for large a and b. + */ + static inline unsigned int safeATimesBDividedByC (unsigned int a, + unsigned int b, + unsigned int c) + { + // TODO This should not work for 64 bit integers. + int z = getZeroBits (a) + getZeroBits (b); + if (z >= 32) + return a * b / c; + else { + //int n = 32 - z, n1 = n / 2, n2 = (n + 1) / 2; + int n = 32 - z, n1 = 0, n2 = n; + return (a >> n1) * (b >> n2) / (c >> n); + } + } + + virtual void accumulateSize (int index, int size, + ::dw::core::Requisition *totalReq, + ::dw::core::Requisition *childReq, + int data1) = 0; + virtual void accumulateExtremes (int index, int size, + ::dw::core::Extremes *totalExtr, + ::dw::core::Extremes *childExtr) = 0; + +public: + static int CLASS_ID; + + Box (bool stretchChildren); + ~Box (); + + ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd); + void removeChild (Widget *child); + + void addChild (::dw::core::Widget *widget, int newPos = -1); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_BOX_HH__ diff --git a/dwr/graph.cc b/dwr/graph.cc new file mode 100644 index 0000000..d8a1b35 --- /dev/null +++ b/dwr/graph.cc @@ -0,0 +1,642 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "graph.hh" +#include "tools.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::container::typed; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Graph::CLASS_ID = -1; + +// ---------------------------------------------------------------------- + +// TODO: GraphIterator ignores the text nodes (for references). Are +// they needed, anyway? +Graph::GraphIterator::GraphIterator (Graph *graph, Content::Type mask, + bool atEnd) : Iterator (graph, mask, atEnd) +{ + index = atEnd ? graph->allWidgets->size() : -1; + content.type = atEnd ? Content::END : Content::START; +} + +Graph::GraphIterator::GraphIterator (Graph *graph, Content::Type mask, + int index) : Iterator (graph, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = Content::START; + else if (index >= graph->allWidgets->size ()) + content.type = Content::END; + else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->allWidgets->get (index); + } +} + +lout::object::Object *Graph::GraphIterator::clone () +{ + return new GraphIterator ((Graph*)getWidget(), getMask(), index); +} + +int Graph::GraphIterator::compareTo (lout::object::Comparable *other) +{ + return index - ((GraphIterator*)other)->index; +} + +bool Graph::GraphIterator::next () +{ + Graph *graph = (Graph*)getWidget(); + + if (content.type == Content::END) + return false; + + // graphs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::END; + return false; + } + + index++; + if (index >= graph->allWidgets->size ()) { + content.type = Content::END; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->allWidgets->get (index); + return true; + } +} + +bool Graph::GraphIterator::prev () +{ + Graph *graph = (Graph*)getWidget(); + + if (content.type == Content::START) + return false; + + // graphs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::START; + return false; + } + + index--; + if (index < 0) { + content.type = Content::START; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->allWidgets->get (index); + return true; + } +} + +void Graph::GraphIterator::highlight (int start, int end, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Graph::GraphIterator::unhighlight (int direction, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Graph::GraphIterator::getAllocation (int start, int end, + Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +// ---------------------------------------------------------------------- + +Graph::Node::Node (Widget *widget, Node *parent) +{ + type = WIDGET; + this->widget = widget; + this->parent = parent; + children = new List<Node> (true); +} + +Graph::Node::Node (const char *text, Node *parent) +{ + type = REF; + this->text = strdup (text); + this->parent = parent; + children = new List<Node> (true); +} + +Graph::Node::~Node () +{ + if (type == WIDGET) { + if (widget) // see Graph::removeChild + delete widget; + } else + free (text); + delete children; +} + +// ---------------------------------------------------------------------- + +Graph::Graph () +{ + DBG_OBJ_CREATE ("rtfl::dw::Graph"); + registerName ("rtfl::dw::Graph", &CLASS_ID); + + allWidgets = new Vector<Widget> (4, false); + topLevelNodes = new List<Node> (true); + refStyle = NULL; + pressedRefNode = NULL; + inDestructor = false; +} + +Graph::~Graph () +{ + inDestructor = true; + delete allWidgets; + delete topLevelNodes; + if (refStyle) + refStyle->unref (); + + DBG_OBJ_DELETE (); +} + +void Graph::sizeRequestImpl (Requisition *requisition) +{ + sizeRequest (requisition, topLevelNodes); + + requisition->width += getStyle()->boxDiffWidth (); + requisition->ascent += getStyle()->boxOffsetY (); + requisition->descent += getStyle()->boxRestHeight (); +} + +void Graph::sizeRequest (Requisition *requisition, List<Node> *list) +{ + // For simlicity, descent is set to 0. (Anyway never needed within + // RTFL.) + requisition->width = requisition->descent = 0; + requisition->ascent = max (VDIFF * (list->size () - 1), 0); + + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + Requisition nodeReq; + sizeRequest (&nodeReq, node); + + requisition->width = max (requisition->width, nodeReq.width); + requisition->ascent += (nodeReq.ascent + nodeReq.descent); + } +} + +void Graph::sizeRequest (Requisition *requisition, Node *node) +{ + if (node->type == Node::WIDGET) + node->widget->sizeRequest (&node->rootReq); + else { + node->rootReq.width = + layout->textWidth (refStyle->font, node->text, strlen (node->text)) + + refStyle->boxDiffWidth (); + node->rootReq.ascent = refStyle->font->ascent + refStyle->boxOffsetY (); + node->rootReq.descent = + refStyle->font->descent + refStyle->boxRestHeight (); + } + + if (node->children->isEmpty ()) + *requisition = node->rootReq; + else { + sizeRequest (&node->childrenReq, node->children); + + requisition->ascent = + max (node->rootReq.ascent + node->rootReq.descent, + node->childrenReq.ascent + node->childrenReq.descent); + requisition->descent = 0; + requisition->width = + node->rootReq.width + HDIFF + node->childrenReq.width; + } +} + +void Graph::getExtremesImpl (Extremes *extremes) +{ + getExtremes (extremes, topLevelNodes); + + extremes->minWidth += getStyle()->boxDiffWidth (); + extremes->maxWidth += getStyle()->boxDiffWidth (); +} + +void Graph::getExtremes (Extremes *extremes, List<Node> *list) +{ + extremes->minWidth = extremes->maxWidth = 0; + + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + Extremes nodeExtr; + getExtremes (&nodeExtr, node); + + extremes->minWidth = max (extremes->minWidth, nodeExtr.minWidth); + extremes->maxWidth = max (extremes->maxWidth, nodeExtr.maxWidth); + } +} + +void Graph::getExtremes (Extremes *extremes, Node *node) +{ + Extremes rootExtr; + if (node->type == Node::WIDGET) + node->widget->getExtremes (&rootExtr); + else + rootExtr.minWidth = rootExtr.maxWidth = + layout->textWidth (refStyle->font, node->text, strlen (node->text)) + + refStyle->boxDiffWidth (); + + if (node->children->isEmpty ()) + *extremes = rootExtr; + else { + Extremes childrenExtr; + getExtremes (&childrenExtr, node->children); + + extremes->minWidth = rootExtr.minWidth + HDIFF + childrenExtr.minWidth; + extremes->maxWidth = rootExtr.maxWidth + HDIFF + childrenExtr.maxWidth; + } +} + +void Graph::sizeAllocateImpl (Allocation *allocation) +{ + // Assumes that sizeRequest has been called before. Only position, + // not allocation size, is considered. + //printf ("sizeAllocate (%d, %d, ...)\n", allocation->x, allocation->y); + sizeAllocate (allocation->x + getStyle()->boxOffsetX (), + allocation->y + getStyle()->boxOffsetY (), topLevelNodes); +} + +void Graph::sizeAllocate (int x, int y, List<Node> *list) +{ + //printf (" List: sizeAllocate (%d, %d, ...)\n", x, y); + + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + sizeAllocate (x, y, node); + int h = node->children->isEmpty () ? + node->rootReq.ascent + node->rootReq.descent : + max (node->rootReq.ascent + node->rootReq.descent, + node->childrenReq.ascent + node->childrenReq.descent); + y += (VDIFF + h); + } +} + +void Graph::sizeAllocate (int x, int y, Node *node) +{ + // Notice that node->childrenReq is undefined when there are no + // children. + + node->rootX = x; + node->rootY = node->children->isEmpty () ? y : + y + max ((node->childrenReq.ascent + node->childrenReq.descent + - (node->rootReq.ascent + node->rootReq.descent)) / 2, 0); + + + if (node->type == Node::WIDGET) { + Allocation rootAlloc; + rootAlloc.x = node->rootX; + rootAlloc.y = node->rootY; + rootAlloc.width = node->rootReq.width; + rootAlloc.ascent = node->rootReq.ascent; + rootAlloc.descent = node->rootReq.descent; + node->widget->sizeAllocate (&rootAlloc); + } + + if (!node->children->isEmpty ()) + sizeAllocate (x + HDIFF + node->rootReq.width, + y + max ((node->rootReq.ascent + node->rootReq.descent + - (node->childrenReq.ascent + + node->childrenReq.descent)) / 2, 0), + node->children); +} + +void Graph::draw (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawWidgetBox (view, area, false); + draw (view, area, topLevelNodes); + + DBG_OBJ_LEAVE (); +} + +void Graph::draw (View *view, Rectangle *area, List<Node> *list) +{ + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + + if (node->type == Node::WIDGET) { + Rectangle childArea; + if (node->widget->intersects (area, &childArea)) + node->widget->draw (view, &childArea); + } else { + drawBox (view, refStyle, area, node->rootX, node->rootY, + node->rootReq.width, + node->rootReq.ascent + node->rootReq.descent, false); + view-> drawText (refStyle->font, refStyle->color, + Color::SHADING_NORMAL, + node->rootX + refStyle->boxOffsetX (), + node->rootY + node->rootReq.ascent, + node->text, strlen (node->text)); + } + + drawArrows (view, area, node); + + draw (view, area, node->children); + } +} + +void Graph::drawArrows (View *view, Rectangle *area, Node *node) +{ + // TODO Regarding "area" could make this faster, especially for + // large graphs. + + int x1 = node->rootX + node->rootReq.width; + int y1 = node->rootY + (node->rootReq.ascent + node->rootReq.descent) / 2; + + for (lout::container::typed::Iterator<Node> it = node->children->iterator (); + it.hasNext (); ) { + Node *child = it.getNext (); + + int x2 = child->rootX; + int y2 = + child->rootY + (child->rootReq.ascent + child->rootReq.descent) / 2; + + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, x1, y1, x2, y2); + tools::drawArrowHead (view, getStyle(), x1, y1, x2, y2, AHEADLEN); + } +} + +::dw::core::Iterator *Graph::iterator (Content::Type mask, bool atEnd) +{ + return new GraphIterator (this, mask, atEnd); +} + +void Graph::removeChild (Widget *child) +{ + if (!inDestructor) { + Node *node = searchWidget (child); + assert (node != NULL); + assert (node->type == Node::WIDGET); + + // Otherwise, Node::~Node would delete the widget again: + Widget *widget = node->widget; + node->widget = NULL; + + // TODO No full implementation, only when all edges have been removed. + assert (node->parent == NULL); + assert (node->children->isEmpty ()); + + bool removed = topLevelNodes->removeRef (node); + assert (removed); + + int pos = -1; + for (int i = 0; pos == -1 && i < allWidgets->size (); i++) { + if (allWidgets->get (i) == widget) + pos = i; + } + + assert (pos != -1); + allWidgets->remove (pos); + } +} + +void Graph::setStyle (Style *style) +{ + Widget::setStyle (style); + if (refStyle == NULL && style != NULL) { + refStyle = style; + refStyle->ref (); + } +} + +Graph::Node *Graph::searchRefNode (MousePositionEvent *event, List<Node> *list) +{ + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + if (event->xCanvas >= node->rootX && + event->xCanvas < node->rootX + node->rootReq.width && + event->yCanvas >= node->rootY && + event->yCanvas < node->rootY + node->rootReq.ascent + + node->rootReq.descent) + return node; + + Node *node2 = searchRefNode (event, node->children); + if (node2) + return node2; + } + + return NULL; +} + +bool Graph::buttonPressImpl (EventButton *event) +{ + if (event->button == 1) { + pressedRefNode = searchRefNode (event); + return pressedRefNode != NULL; + } else + return false; +} + +bool Graph::buttonReleaseImpl (EventButton *event) +{ + if (event->button == 1 && pressedRefNode != NULL) { + Node *newRefNode = searchRefNode (event); + if (newRefNode == pressedRefNode) { + Node *ref = pressedRefNode->reference; + scrollTo (HPOS_CENTER, VPOS_CENTER, ref->rootX + getAllocation()->x, + ref->rootY + getAllocation()->y, ref->rootReq.width, + ref->rootReq.ascent + ref->rootReq.descent); + pressedRefNode = NULL; + } + return true; + } else + return false; +} + +bool Graph::motionNotifyImpl (::dw::core::EventMotion *event) +{ + if ((event->state & BUTTON1_MASK) && pressedRefNode) { + Node *newRefNode = searchRefNode (event); + if (newRefNode != pressedRefNode) + pressedRefNode = NULL; + } + return false; +} + +void Graph::leaveNotifyImpl (EventCrossing *event) +{ + pressedRefNode = NULL; +} + +bool Graph::isAncestorOf (Node *n1, Node *n2) +{ + for (Node *node = n2; node; node = node->parent) + if (node == n1) + return true; + + return false; +} + +void Graph::addReference (Node *from, Node *to) +{ + assert (from->type == Node::WIDGET); + assert (to->type == Node::WIDGET); + //printf ("edge from %s %p to %s %p implemented as reference.\n", + // from->widget->getClassName (), from->widget, + // to->widget->getClassName (), to->widget); + + Node *ref1 = new Node ("…", from); + from->children->append (ref1); + + Node *ref2 = new Node ("…", to); + List<Node> *list = to->parent ? to->parent->children : topLevelNodes; + list->insertBefore (to, ref2); + list->detachRef (to); + ref2->children->append (to); + + ref1->reference = ref2; + ref2->reference = ref1; +} + +void Graph::addNode (Widget *widget) +{ + if (searchWidget (widget)) + fprintf (stderr, "WARNING: widget %p added twice.\n", widget); + else { + allWidgets->put (widget); + topLevelNodes->append (new Node (widget, NULL)); + widget->setParent (this); + queueResize (0, true); + } +} + +void Graph::addEdge (Widget *from, Widget *to) +{ + Node *nodeFrom, *nodeTo; + + if ((nodeFrom = searchWidget (from)) == NULL) { + fprintf (stderr, "WARNING: adding edge starting from unknown widget %p\n", + from); + return; + } + + if ((nodeTo = searchWidget (to)) == NULL) { + fprintf (stderr, "WARNING: adding edge ending in unknown widget %p\n", + to); + return; + } + + if (nodeTo->parent) { + if (nodeTo->parent == nodeFrom) + fprintf (stderr, + "WARNING: edge from widget %p to widget %p added twice.\n", + from, to); + else { + // Second node is already the end of an edge, so no new is + // added, but a reference. + addReference (nodeFrom, nodeTo); + } + } else { + // Here, nodeTo->parent is NULL. + if (isAncestorOf (nodeTo, nodeFrom)) + // Would cause a cycle otherwise. + addReference (nodeFrom, nodeTo); + else { + topLevelNodes->detachRef (nodeTo); + nodeTo->parent = nodeFrom; + nodeFrom->children->append (nodeTo); + } + } + + queueResize (0, true); +} + +void Graph::removeEdge (::dw::core::Widget *from, ::dw::core::Widget *to) +{ + Node *nodeFrom, *nodeTo; + + if ((nodeFrom = searchWidget (from)) == NULL) { + fprintf (stderr, "WARNING: adding edge starting from unknown widget %p\n", + from); + return; + } + + if ((nodeTo = searchWidget (to)) == NULL) { + fprintf (stderr, "WARNING: adding edge ending in unknown widget %p\n", + to); + return; + } + + assertNotReached (); // TODO + + queueResize (0, true); +} + +Graph::Node *Graph::searchWidget (Widget *widget, List<Node> *list) +{ + for (lout::container::typed::Iterator<Node> it = list->iterator (); + it.hasNext (); ) { + Node *node = it.getNext (); + if (node->type == Node::WIDGET && node->widget == widget) + return node; + + Node *node2 = searchWidget (widget, node->children); + if (node2) + return node2; + } + + return NULL; +} + +void Graph::setRefStyle (::dw::core::style::Style *style) +{ + if (refStyle) + refStyle->unref (); + + refStyle = style; + refStyle->ref (); +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/graph.hh b/dwr/graph.hh new file mode 100644 index 0000000..7e0897e --- /dev/null +++ b/dwr/graph.hh @@ -0,0 +1,126 @@ +#ifndef __DWR_GRAPH_HH__ +#define __DWR_GRAPH_HH__ + +#include "dw/core.hh" +#include "lout/misc.hh" + +namespace rtfl { + +namespace dw { + +class Graph: public ::dw::core::Widget +{ +private: + class GraphIterator: public ::dw::core::Iterator + { + private: + int index; + + GraphIterator (Graph *graph, ::dw::core::Content::Type mask, int index); + + public: + GraphIterator (Graph *graph, ::dw::core::Content::Type mask, bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, ::dw::core::HighlightLayer layer); + void unhighlight (int direction, ::dw::core::HighlightLayer layer); + void getAllocation (int start, int end, + ::dw::core::Allocation *allocation); + }; + + class Node: public lout::object::Object + { + public: + enum { WIDGET, REF } type; + union { + ::dw::core::Widget *widget; + struct { + char *text; + Node *reference; + }; + }; + Node *parent; + lout::container::typed::List<Node> *children; + + int rootX, rootY; + ::dw::core::Requisition rootReq, childrenReq; + + Node (::dw::core::Widget *widget, Node *parent); + Node (const char *text, Node *parent); + ~Node (); + }; + + enum { HDIFF = 50, VDIFF = 20, AHEADLEN = 10 }; + + lout::container::typed::Vector<Widget> *allWidgets; + lout::container::typed::List<Node> *topLevelNodes; + ::dw::core::style::Style *refStyle; + Node *pressedRefNode; + bool inDestructor; + + Node *searchWidget (::dw::core::Widget *widget, + lout::container::typed::List<Node> *list); + inline Node *searchWidget (::dw::core::Widget *widget) + { return searchWidget (widget, topLevelNodes); } + + void sizeRequest (::dw::core::Requisition *requisition, + lout::container::typed::List<Node> *list); + void sizeRequest (::dw::core::Requisition *requisition, Node *node); + void getExtremes (::dw::core::Extremes *extremes, + lout::container::typed::List<Node> *list); + void getExtremes (::dw::core::Extremes *extremes, Node *node); + void sizeAllocate (int x, int y, lout::container::typed::List<Node> *list); + void sizeAllocate (int x, int y, Node *node); + + void draw (::dw::core::View *view, ::dw::core::Rectangle *area, + lout::container::typed::List<Node> *list); + void drawArrows (::dw::core::View *view, ::dw::core::Rectangle *area, + Node *node); + + bool isAncestorOf (Node *n1, Node *n2); + void addReference (Node *from, Node *to); + + // TODO Not very efficient. Since there are not many ref nodes, + // they could be kept in one list. + Node *searchRefNode (::dw::core::MousePositionEvent *event) + { return searchRefNode (event, topLevelNodes); } + Node *searchRefNode (::dw::core::MousePositionEvent *event, + lout::container::typed::List<Node> *list); + +protected: + void sizeRequestImpl (::dw::core::Requisition *requisition); + void getExtremesImpl (::dw::core::Extremes *extremes); + void sizeAllocateImpl (::dw::core::Allocation *allocation); + + bool buttonPressImpl (::dw::core::EventButton *event); + bool buttonReleaseImpl (::dw::core::EventButton *event); + bool motionNotifyImpl (::dw::core::EventMotion *event); + void leaveNotifyImpl (::dw::core::EventCrossing *event); + +public: + static int CLASS_ID; + + Graph (); + ~Graph (); + + void draw (::dw::core::View *view, ::dw::core::Rectangle *area); + ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd); + void removeChild (Widget *child); + void setStyle (::dw::core::style::Style *style); + + void addNode (::dw::core::Widget *widget); + void addEdge (::dw::core::Widget *from, ::dw::core::Widget *to); + void removeEdge (::dw::core::Widget *from, ::dw::core::Widget *to); + + void setRefStyle (::dw::core::style::Style *style); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_GRAPH_HH__ diff --git a/dwr/graph2.cc b/dwr/graph2.cc new file mode 100644 index 0000000..28f03f9 --- /dev/null +++ b/dwr/graph2.cc @@ -0,0 +1,505 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* ---------------------------------------------------------------------- + + This is an experimenal graph widget which replaces the widget Graph + currently used by rtfl-objview. It uses Graphviz for layouting; in + detail, pixels and points (1 point = 1/72 of an inch) are treated + as identical, for attributes "bb", "pos" etc. ("width" and "height" + are given in inch, so these values are calculated by division by + 72). + + See <http://www.graphviz.org/doc/info/attrs.html> for informations + on Graphviz attributes. + + Open issues: + + - The order of the children should be preserved. When B1, B2 and B3 + are associated to A, in this order, this order should be visible, + even at the expense of other factors. + + (My original idea was to implement this widget from scratch, + using a force directed approach with + + (i) invisible edges between the "children" (in the example + above: B1 -> B2 and B2 -> B3), which cause attraction of + the respective nodes in a similar way the visible nodes do + ("springs"); + + (ii) a homogeneous force field directed to the right and acting + on the heads of the *visible* edges, so giving the graph + layout an orientation from left to right; + + (iii) a second homogeneous force field directed to the bottom, + which acts only on the heads of the *invisible* edges. + + (i) and (iii) would lead to the desired result.) + + Update: setting "ordering" to "out" is a step into this + direction. (This seems to enforce the order of the edges, not the + nodes.) + + - Configurability: the algorithm (currently "dot") and some more + parameters (like "rankdir=LR") could be made configurable. + + ---------------------------------------------------------------------- */ + +#include "graph2.hh" +#include "tools.hh" +#include "../common/tools.hh" +#include "../lout/misc.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::container::typed; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Graph2::CLASS_ID = -1; + +Graph2::Node::Node (Graph2 *graph, Widget *widget, int index) +{ + this->graph = graph; + this->widget = widget; + this->index = index; + + initAg (); +} + +Graph2::Node::~Node () +{ + if (widget) + delete widget; + + cleanupAg (); +} + +void Graph2::Node::initAg () +{ + assert (graph->graph); + + char buf[64]; + snprintf (buf, 64, "n%d", index); + node = agnode(graph->graph, buf, TRUE); + agsafeset (node, (char*)"shape", (char*)"rect", (char*)""); +} + +void Graph2::Node::cleanupAg () +{ + if (node) { + assert (graph->graph); + agdelete (graph->graph, node); + node = NULL; + } +} + +Graph2::Edge::Edge (Graph2 *graph, Node *from, Node *to, int index) +{ + this->graph = graph; + this->from = from; + this->to = to; + this->index = index; + + initAg (); + + numPoints = numPointsAlloc = 0; + pointX = NULL; + pointY = NULL; + pointType = NULL; + + count = 1; +} + +Graph2::Edge::~Edge () +{ + cleanupAg (); + cleanupPoints (); +} + +void Graph2::Edge::initAg () +{ + assert (graph->graph); + + char buf[64]; + snprintf (buf, 64, "e%d", index); + edge = agedge (graph->graph, from->node, to->node, (char*)buf, TRUE); +} + +void Graph2::Edge::cleanupAg () +{ + if (edge) { + assert (graph->graph); + agdelete (graph->graph, edge); + edge = NULL; + } +} + +void Graph2::Edge::cleanupPoints () +{ + if (numPointsAlloc > 0) { + delete[] pointX; + delete[] pointY; + delete[] pointType; + + pointX = NULL; + pointY = NULL; + pointType = NULL; + + numPointsAlloc = 0; + } +} + +void Graph2::Edge::setNumPoints (int numPoints) +{ + if (numPoints > numPointsAlloc) { + cleanupPoints (); + + numPointsAlloc = numPoints; + + pointX = new int[numPointsAlloc]; + pointY = new int[numPointsAlloc]; + pointType = new char[numPointsAlloc]; + } + + this->numPoints = numPoints; +} + +void Graph2::Edge::sortPoints () +{ + // If a start point exists, it is at the beginning. If an end point + // exists, it is also at the beginning or follows the start point. + // Here, it is moved at the end of the list. + + if ((numPoints >= 1 && pointType[0] == 'e') || + (numPoints >= 2 && pointType[1] == 'e')) { + int epos = pointType[0] == 'e' ? 0 : 1; + int ex = pointX[epos], ey = pointY[epos]; + + for (int i = epos; i < numPoints - 1; i++) { + pointX[i] = pointX[i + 1]; + pointY[i] = pointY[i + 1]; + pointType[i] = pointType[i + 1]; + } + + pointX[numPoints - 1] = ex; + pointY[numPoints - 1] = ey; + pointType[numPoints - 1] = 'e'; + } +} + +Graph2::Graph2 () +{ + DBG_OBJ_CREATE ("rtfl::dw:"###); + registerName ("rtfl::dw::Graph2", &CLASS_ID); + + nodes = new Vector<Node> (4, true); + edges = new Vector<Edge> (4, true); + + initAg (); +} + +Graph2::~Graph2 () +{ + inDestructor = true; + + cleanupAg (); + + delete nodes; + delete edges; + + DBG_OBJ_DELETE (); +} + +void Graph2::initAg () +{ + gvc = gvContext (); + graph = agopen ((char*)"", Agdirected, NULL); + agsafeset (graph, (char*)"rankdir", (char*)"LR", (char*)""); + agsafeset (graph, (char*)"ordering", (char*)"out", (char*)""); + + for (int i = 0; i < nodes->size (); i++) + nodes->get(i)->initAg (); + + for (int i = 0; i < edges->size (); i++) + edges->get(i)->initAg (); +} + +void Graph2::cleanupAg () +{ + for (int i = 0; i < nodes->size (); i++) + nodes->get(i)->cleanupAg (); + + for (int i = 0; i < edges->size (); i++) + edges->get(i)->cleanupAg (); + + if (graph) { + agclose (graph); + graph = NULL; + } + + if (gvc) { + gvFreeContext(gvc); + gvc = NULL; + } +} + +void Graph2::sizeRequestImpl (Requisition *requisition) +{ + for (int i = 0; i < nodes->size (); i++) { + Node *node = nodes->get (i); + + Requisition childReq; + node->widget->sizeRequest (&childReq); + + char buf[64]; + snprintf (buf, 64, "%f", (float)childReq.width / 72); + agsafeset (node->node, (char*)"width", buf, (char*)""); + snprintf (buf, 64, "%f", + (float)(childReq.ascent + childReq.descent) / 72); + agsafeset (node->node, (char*)"height", buf, (char*)""); + } + + //puts ("---------- before layouting ----------"); + //agwrite (graph, stdout); + + gvLayout (gvc, graph, "dot"); + gvRender(gvc, graph, "dot", NULL); + gvFreeLayout(gvc, graph); + + //puts ("---------- after layouting ----------"); + //agwrite (graph, stdout); + + float x, y, width, height; + sscanf (agget (graph, (char*)"bb"), "%f,%f,%f,%f", &x, &y, &width, &height); + + requisition->width = width + getStyle()->boxDiffWidth (); + requisition->ascent = height + getStyle()->boxOffsetY (); + requisition->descent = getStyle()->boxRestHeight (); +} + +void Graph2::getExtremesImpl (Extremes *extremes) +{ + assertNotReached (); +} + +void Graph2::sizeAllocateImpl (Allocation *allocation) +{ + //agwrite (graph, stdout); + + Requisition req; + sizeRequest (&req); + + for (int i = 0; i < nodes->size (); i++) { + Node *node = nodes->get (i); + + Requisition childReq; + node->widget->sizeRequest (&childReq); + + float x, y; + sscanf (agget (node->node, (char*)"pos"), "%f,%f", &x, &y); + + Allocation childAlloc; + childAlloc.x = getStyle()->boxOffsetX () + x - childReq.width / 2; + childAlloc.y = getStyle()->boxOffsetY () + + (req.ascent + req.descent - getStyle()->boxDiffHeight ()) + - y - (childReq.ascent + childReq.descent) / 2; + childAlloc.width = childReq.width; + childAlloc.ascent = childReq.ascent; + childAlloc.descent = childReq.descent; + node->widget->sizeAllocate (&childAlloc); + } + + for (int i = 0; i < edges->size (); i++) { + Edge *edge = edges->get (i); + + char *pos = agget (edge->edge, (char*)"pos"); + int lenPos = strlen (pos); + char *posBuf = new char[lenPos + 1]; + strncpy (posBuf, pos, lenPos + 1); + + int numSpaces = 0; + for (char *s = posBuf; *s; s++) + if (*s == ' ') + numSpaces++; + + edge->setNumPoints (numSpaces + 1); + + int noPoint = 0; + bool atEnd = false; + for (char *start = posBuf; !atEnd; ) { + char *end = start; + while (*end != ' ' && *end != 0) + end++; + atEnd = (*end == 0); + + *end = 0; + + char pointType, *posStart; + if ((start[0] == 's' || start[0] == 'e') && start[1] == ',') { + pointType = start[0]; + posStart = start + 2; + } else { + pointType = 0; + posStart = start; + } + + float x, y; + sscanf (posStart, "%f,%f", &x, &y); + + edge->pointX[noPoint] = getStyle()->boxOffsetX () + x; + edge->pointY[noPoint] = getStyle()->boxOffsetY () + + (req.ascent + req.descent - getStyle()->boxDiffHeight ()) - y; + edge->pointType[noPoint] = pointType; + + noPoint++; + start = end + 1; + } + + edge->sortPoints (); + + delete[] posBuf; + } +} + +void Graph2::draw (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawWidgetBox (view, area, false); + + for (int i = 0; i < nodes->size (); i++) { + Widget *widget = nodes->get(i)->widget; + Rectangle childArea; + if (widget->intersects (area, &childArea)) + widget->draw (view, &childArea); + } + + for (int i = 0; i < edges->size (); i++) { + Edge *edge = edges->get (i); + + tools::drawBSpline (view, getStyle(), 4, edge->numPoints, edge->pointX, + edge->pointY); + + for (int j = 0; j < edge->numPoints - 1; j++) { + // TODO: arrow heads should only be drawn at the first and the last + // point. In this case, the direction is correct; in other cases, the + // the points are not even part of the curve. + + if (edge->pointType[j] == 'e') + tools::drawArrowHead (view, getStyle (), + edge->pointX[j + 1], edge->pointY[j + 1], + edge->pointX[j], edge->pointY[j], + AHEADLEN); + + if (edge->pointType[j + 1] == 'e') + tools::drawArrowHead (view, getStyle (), + edge->pointX[j], edge->pointY[j], + edge->pointX[j + 1], edge->pointY[j + 1], + AHEADLEN); + } + } + + DBG_OBJ_LEAVE (); +} + +::dw::core::Iterator *Graph2::iterator (Content::Type mask, bool atEnd) +{ + return new Graph2Iterator (this, mask, atEnd); +} + +void Graph2::removeChild (Widget *child) +{ + if (!inDestructor) { + int nodeIndex = searchNodeIndex (child); + Node *node = nodes->get (nodeIndex); + + // Otherwise, Node::~Node would delete the widget again: + node->widget = NULL; + + for (int i = 0; i < edges->size (); i++) { + Edge *edge = edges->get (i); + if (edge->from == node || edge->to == node) + edges->remove (i); + } + + nodes->remove (nodeIndex); + + queueResize (0, true); + } +} + +void Graph2::addNode (Widget *widget) +{ + Node *node = new Node (this, widget, nodes->size ()); + nodes->put (node); + + widget->setParent (this); + + queueResize (0, true); +} + +void Graph2::addEdge (Widget *from, Widget *to) +{ + for (int i = 0; i < edges->size (); i++) { + Edge *edge = edges->get (i); + if (edge->from->widget == from && edge->to->widget == to) { + edge->count++; + printf ("WARNING: Edge already added the %d%s time.\n", edge->count, + rtfl::tools::numSuffix (edge->count)); + return; + } + } + + Edge *edge = + new Edge (this, searchNode (from), searchNode (to), edges->size ()); + edges->put (edge); + + queueResize (0, true); +} + +int Graph2::searchNodeIndex (Widget *widget) +{ + for (int i = 0; i < nodes->size (); i++) { + Node *node = nodes->get (i); + if (node->widget == widget) + return i; + } + + assertNotReached (); + return 0; +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/graph2.hh b/dwr/graph2.hh new file mode 100644 index 0000000..128ba32 --- /dev/null +++ b/dwr/graph2.hh @@ -0,0 +1,122 @@ +#ifndef __DWR_GRAPH2_HH__ +#define __DWR_GRAPH2_HH__ + +#include <graphviz/gvc.h> + +#include "dw/core.hh" + +namespace rtfl { + +namespace dw { + +class Graph2: public ::dw::core::Widget +{ +private: + class Graph2Iterator: public ::dw::core::Iterator + { + private: + int index; + + Graph2Iterator (Graph2 *graph, ::dw::core::Content::Type mask, int index); + + public: + Graph2Iterator (Graph2 *graph, ::dw::core::Content::Type mask, + bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, ::dw::core::HighlightLayer layer); + void unhighlight (int direction, ::dw::core::HighlightLayer layer); + void getAllocation (int start, int end, + ::dw::core::Allocation *allocation); + }; + + class Node: public lout::object::Object + { + private: + Graph2 *graph; + int index; + + public: + Node (Graph2 *graph, Widget *widget, int index); + ~Node (); + + void initAg (); + void cleanupAg (); + + Widget *widget; + Agnode_t *node; + }; + + class Edge: public lout::object::Object + { + private: + Graph2 *graph; + int numPointsAlloc, index; + + void cleanupPoints (); + + public: + Edge (Graph2 *graph, Node *from, Node *to, int index); + ~Edge (); + + void initAg (); + void cleanupAg (); + + void setNumPoints (int numPoints); + void sortPoints (); + + Node *from, *to; + Agedge_t *edge; + int numPoints; + int *pointX, *pointY; + char *pointType; + + int count; + }; + + enum { AHEADLEN = 10 }; + + Agraph_t *graph; + GVC_t *gvc; + lout::container::typed::Vector<Node> *nodes; + lout::container::typed::Vector<Edge> *edges; + bool inDestructor; + + void initAg (); + void cleanupAg (); + + int searchNodeIndex (Widget *widget); + inline Node *searchNode (Widget *widget) + { return nodes->get (searchNodeIndex (widget)); } + +protected: + void sizeRequestImpl (::dw::core::Requisition *requisition); + void getExtremesImpl (::dw::core::Extremes *extremes); + void sizeAllocateImpl (::dw::core::Allocation *allocation); + +public: + static int CLASS_ID; + + Graph2 (); + ~Graph2 (); + + void draw (::dw::core::View *view, ::dw::core::Rectangle *area); + ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd); + void removeChild (Widget *child); + + inline void setRefStyle (::dw::core::style::Style *style) + { /* No need anymore. */ } + + void addNode (Widget *widget); + void addEdge (::dw::core::Widget *from, ::dw::core::Widget *to); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_GRAPH2_HH__ diff --git a/dwr/graph2_iterator.cc b/dwr/graph2_iterator.cc new file mode 100644 index 0000000..d546d39 --- /dev/null +++ b/dwr/graph2_iterator.cc @@ -0,0 +1,139 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "graph2.hh" + +using namespace dw::core; + +namespace rtfl { + +namespace dw { + +Graph2::Graph2Iterator::Graph2Iterator (Graph2 *graph, Content::Type mask, + bool atEnd) : + Iterator (graph, mask, atEnd) +{ + index = atEnd ? graph->nodes->size() : -1; + content.type = atEnd ? Content::END : Content::START; +} + +Graph2::Graph2Iterator::Graph2Iterator (Graph2 *graph, Content::Type mask, + int index) : + Iterator (graph, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = Content::START; + else if (index >= graph->nodes->size ()) + content.type = Content::END; + else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->nodes->get(index)->widget; + } +} + +lout::object::Object *Graph2::Graph2Iterator::clone () +{ + return new Graph2Iterator ((Graph2*)getWidget(), getMask(), index); +} + +int Graph2::Graph2Iterator::compareTo (lout::object::Comparable *other) +{ + return index - ((Graph2Iterator*)other)->index; +} + +bool Graph2::Graph2Iterator::next () +{ + Graph2 *graph = (Graph2*)getWidget(); + + if (content.type == Content::END) + return false; + + // graphs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::END; + return false; + } + + index++; + if (index >= graph->nodes->size ()) { + content.type = Content::END; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->nodes->get(index)->widget; + return true; + } +} + +bool Graph2::Graph2Iterator::prev () +{ + Graph2 *graph = (Graph2*)getWidget(); + + if (content.type == Content::START) + return false; + + // graphs only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::START; + return false; + } + + index--; + if (index < 0) { + content.type = Content::START; + return false; + } else { + content.type = Content::WIDGET_IN_FLOW; + content.widget = graph->nodes->get(index)->widget; + return true; + } +} + +void Graph2::Graph2Iterator::highlight (int start, int end, + HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Graph2::Graph2Iterator::unhighlight (int direction, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Graph2::Graph2Iterator::getAllocation (int start, int end, + Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/hbox.cc b/dwr/hbox.cc new file mode 100644 index 0000000..6184078 --- /dev/null +++ b/dwr/hbox.cc @@ -0,0 +1,123 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "hbox.hh" + +using namespace dw::core; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int HBox::CLASS_ID = -1; + +HBox::HBox (bool stretchChildren): Box (stretchChildren) +{ + DBG_OBJ_CREATE ("rtfl::dw::HBox"); + registerName ("rtfl::dw::HBox", &CLASS_ID); +} + +HBox::~HBox () +{ + DBG_OBJ_DELETE (); +} + +void HBox::sizeAllocateImpl (Allocation *allocation) +{ + Allocation allocWOBorders; + allocWOBorders.x = allocation->x + getStyle()->boxOffsetX (); + allocWOBorders.y = allocation->y + getStyle()->boxOffsetY (); + allocWOBorders.width = allocation->width - getStyle()->boxDiffWidth (); + allocWOBorders.ascent = allocation->ascent - getStyle()->boxOffsetY (); + allocWOBorders.descent = allocation->descent - getStyle()->boxRestHeight (); + + int sumReqWidth = 0; + for (int i = 0; i < children->size (); i++) { + Requisition childReq; + children->get(i)->sizeRequest (&childReq); + sumReqWidth += childReq.width; + } + + bool stretch = stretchChildren && sumReqWidth < allocWOBorders.width; + + // Cf. doc/rounding-errors.doc, with: x[i] = child requisition, + // y[i] = child allocation, a = total allocation, b = sumReqWidth. + int cumChildReqWidth = 0, cumChildAllocWidth = 0; + for (int i = 0; i < children->size (); i++) { + Widget *child = children->get (i); + + Requisition childReq; + child->sizeRequest (&childReq); + + Allocation childAlloc; + childAlloc.x = allocWOBorders.x + cumChildAllocWidth; + childAlloc.y = allocWOBorders.y; + + if (stretch) { + cumChildReqWidth += childReq.width; + childAlloc.width = sumReqWidth > 0 ? + safeATimesBDividedByC (cumChildReqWidth, allocWOBorders.width, + sumReqWidth) - cumChildAllocWidth : + 0; + childAlloc.ascent = allocWOBorders.ascent; + childAlloc.descent = allocWOBorders.descent; + } else + childAlloc.width = childReq.width; + + childAlloc.ascent = allocWOBorders.ascent; + childAlloc.descent = allocWOBorders.descent; + cumChildAllocWidth += childAlloc.width; + + child->sizeAllocate (&childAlloc); + + //printf ("hbox: %dth child at (%d, %d), %d, (%d x %d)\n", + // i, childAlloc.x, childAlloc.y, childAlloc.width, + // childAlloc.ascent, childAlloc.descent); + } +} + +void HBox::accumulateSize (int index, int size, Requisition *totalReq, + Requisition *childReq, int data1) +{ + totalReq->width += childReq->width; + totalReq->ascent = max (totalReq->ascent, childReq->ascent); + totalReq->descent = max (totalReq->descent, childReq->descent); +} + +void HBox::accumulateExtremes (int index, int size, Extremes *totalExtr, + Extremes *childExtr) +{ + totalExtr->minWidth += childExtr->minWidth; + totalExtr->maxWidth += childExtr->maxWidth; +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/hbox.hh b/dwr/hbox.hh new file mode 100644 index 0000000..79b71ff --- /dev/null +++ b/dwr/hbox.hh @@ -0,0 +1,31 @@ +#ifndef __DWR_HBOX_HH__ +#define __DWR_HBOX_HH__ + +#include "box.hh" + +namespace rtfl { + +namespace dw { + +class HBox: public Box +{ +protected: + void sizeAllocateImpl (::dw::core::Allocation *allocation); + void accumulateSize (int index, int size, ::dw::core::Requisition *totalReq, + ::dw::core::Requisition *childReq, int data1); + void accumulateExtremes (int index, int size, + ::dw::core::Extremes *totalExtr, + ::dw::core::Extremes *childExtr); + +public: + static int CLASS_ID; + + HBox (bool stretchChildren); + ~HBox (); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_HBOX_HH__ diff --git a/dwr/hideable.cc b/dwr/hideable.cc new file mode 100644 index 0000000..def90a5 --- /dev/null +++ b/dwr/hideable.cc @@ -0,0 +1,104 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "hideable.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Hideable::CLASS_ID = -1; + +Hideable::Hideable () +{ + DBG_OBJ_CREATE ("rtfl::dw::Hideable"); + registerName ("rtfl::dw::Hideable", &CLASS_ID); + + hidden = drawable = false; +} + +Hideable::~Hideable () +{ + DBG_OBJ_DELETE (); +} + +void Hideable::sizeRequestImpl (Requisition *requisition) +{ + if (hidden) { + requisition->width = requisition->ascent = requisition->descent = 0; + drawable = false; + } else { + sizeRequestImplImpl (requisition); + drawable = true; + } +} + +void Hideable::getExtremesImpl (Extremes *extremes) +{ + if (hidden) + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = 0; + else + getExtremesImplImpl (extremes); +} + +void Hideable::draw (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + if (drawable) + drawImpl (view, area); + + DBG_OBJ_LEAVE (); +} + +void Hideable::show () +{ + if (hidden) { + hidden = false; + queueResize (0, true); + } +} + +void Hideable::hide () +{ + if (!hidden) { + hidden = true; + queueResize (0, true); + } +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/hideable.hh b/dwr/hideable.hh new file mode 100644 index 0000000..aabf807 --- /dev/null +++ b/dwr/hideable.hh @@ -0,0 +1,39 @@ +#ifndef __DWR_HIDEABLE_HH__ +#define __DWR_HIDEABLE_HH__ + +#include "dw/core.hh" + +namespace rtfl { + +namespace dw { + +class Hideable: public ::dw::core::Widget +{ + bool hidden, drawable; + +protected: + void sizeRequestImpl (::dw::core::Requisition *requisition); + void getExtremesImpl (::dw::core::Extremes *extremes); + + virtual void sizeRequestImplImpl (::dw::core::Requisition *requisition) = 0; + virtual void getExtremesImplImpl (::dw::core::Extremes *extremes) = 0; + virtual void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area) + = 0; + +public: + static int CLASS_ID; + + Hideable (); + ~Hideable (); + + void draw (::dw::core::View *view, ::dw::core::Rectangle *area); + + void show (); + void hide (); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_HIDEABLE_HH__ diff --git a/dwr/label.cc b/dwr/label.cc new file mode 100644 index 0000000..dfef145 --- /dev/null +++ b/dwr/label.cc @@ -0,0 +1,378 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "label.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Label::CLASS_ID = -1; + +// ---------------------------------------------------------------------- + +Label::LabelIterator::LabelIterator (Label *label, Content::Type mask, + bool atEnd) : Iterator (label, mask, atEnd) +{ + index = atEnd ? label->words->size() : -1; + content.type = atEnd ? Content::END : Content::START; +} + +Label::LabelIterator::LabelIterator (Label *label, Content::Type mask, + int index) : Iterator (label, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = Content::START; + else if (index >= label->words->size ()) + content.type = Content::END; + else { + content.type = Content::TEXT; + content.text = label->words->getRef(index)->text; + } +} + +lout::object::Object *Label::LabelIterator::clone() +{ + return new LabelIterator ((Label*)getWidget(), getMask(), index); +} + +int Label::LabelIterator::compareTo(lout::object::Comparable *other) +{ + return index - ((LabelIterator*)other)->index; +} + +bool Label::LabelIterator::next () +{ + Label *label = (Label*)getWidget(); + + if (content.type == Content::END) + return false; + + // labels only contain widgets: + if ((getMask() & Content::TEXT) == 0) { + content.type = Content::END; + return false; + } + + index++; + if (index >= label->words->size ()) { + content.type = Content::END; + return false; + } else { + content.type = Content::TEXT; + content.text = label->words->getRef(index)->text; + return true; + } +} + +bool Label::LabelIterator::prev () +{ + Label *label = (Label*)getWidget(); + + if (content.type == Content::START) + return false; + + // labels only contain widgets: + if ((getMask() & Content::TEXT) == 0) { + content.type = Content::START; + return false; + } + + index--; + if (index < 0) { + content.type = Content::START; + return false; + } else { + content.type = Content::TEXT; + content.text = label->words->getRef(index)->text; + return true; + } +} + +void Label::LabelIterator::highlight (int start, int end, HighlightLayer layer) +{ + /** \bug Not implemented. */ +} + +void Label::LabelIterator::unhighlight (int direction, HighlightLayer layer) +{ + /** \bug Not implemented. */ +} + +void Label::LabelIterator::getAllocation (int start, int end, + Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +// ---------------------------------------------------------------------- + +Label::Label (const char *text, int link) +{ + DBG_OBJ_CREATE ("rtfl::dw::Label"); + registerName ("rtfl::dw::Label", &CLASS_ID); + + words = new SimpleVector<Word> (1); + + for (int i = 0; i < 4; i++) + styles[i] = NULL; + + selected = buttonDown = false; + this->link = link; + + setText (text); +} + +Label::~Label () +{ + clearWords (); + delete words; + + clearStyles (); + + DBG_OBJ_DELETE (); +} + +void Label::setText (const char *text) +{ + clearWords (); + + DBG_OBJ_SET_STR ("text", text); + + // Parse text for tags <i>, </i>, <b>, </b>. Very simple, no stack. + const char *start = text; + int styleIndex = 0; + while (*start) { + const char *end = start; + + while (!(*end == 0 || + strncmp (end, "<i>", 3) == 0 || strncmp (end, "<b>", 3) == 0 || + strncmp (end, "</i>", 4) == 0 || strncmp (end, "</b>", 4) == 0)) + end++; + + //printf ("start: '%s', end: '%s'\n", start, end); + + if (end > start) { + words->increase (); + Word *word = words->getLastRef (); + word->text = strndup (start, end - start); + word->styleIndex = styleIndex; + + //printf (" new word '%s' with attributes %c%c\n", word->text, + // (word->styleIndex & ITALIC) ? 'i' : '-', + // (word->styleIndex & BOLD) ? 'b' : '-'); + } + + if (*end == 0) + start = end; + else if (strncmp (end, "<i>", 3) == 0) { + start = end + 3; + styleIndex |= ITALIC; + } else if (strncmp (end, "<b>", 3) == 0) { + start = end + 3; + styleIndex |= BOLD; + } else if (strncmp (end, "</i>", 4) == 0) { + start = end + 4; + styleIndex &= ~ITALIC; + } else if (strncmp (end, "</b>", 4) == 0) { + start = end + 4; + styleIndex &= ~BOLD; + } else + assertNotReached (); + } + + queueResize (0, true); +} + +void Label::clearWords () +{ + for (int i = 0; i < words->size (); i++) + free (words->getRef(i)->text); + words->setSize (0); +} + +void Label::clearStyles () +{ + for (int i = 0; i < 4; i++) + if (styles[i]) { + styles[i]->unref (); + styles[i] = NULL; + } +} + +void Label::ensureStyles () +{ + if (getStyle () && styles[0] == NULL) { + styles[0] = getStyle (); + styles[0]->ref (); + + for (int i = 1; i < 4; i++) { + StyleAttrs styleAttrs = *getStyle (); + FontAttrs fontAttrs = *(styleAttrs.font); + fontAttrs.weight = (i & BOLD) ? 700 : 400; + fontAttrs.style = (i & ITALIC) ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL; + styleAttrs.font = Font::create (layout, &fontAttrs); + styles[i] = Style::create (&styleAttrs); + } + } +} + +void Label::sizeRequestImplImpl (Requisition *requisition) +{ + totalSize.width = totalSize.ascent = totalSize.descent = 0; + + if (getStyle ()) { + ensureStyles (); + + for (int i = 0; i < words->size (); i++) { + Word *word = words->getRef(i); + Font *font = styles[(int)word->styleIndex]->font; + + word->size.width = + layout->textWidth (font, word->text, strlen (word->text)); + word->size.ascent = font->ascent; + word->size.descent = font->descent; + + totalSize.width += word->size.width; + totalSize.ascent = max (totalSize.ascent, word->size.ascent); + totalSize.descent = max (totalSize.descent, word->size.descent); + } + } else + totalSize.width = totalSize.ascent = totalSize.descent = 0; + + requisition->width = totalSize.width + getStyle()->boxDiffWidth (); + requisition->ascent = totalSize.ascent + getStyle()->boxOffsetY (); + requisition->descent = totalSize.descent + getStyle()->boxRestHeight (); +} + +void Label::getExtremesImplImpl (Extremes *extremes) +{ + // Not used within RTFL. + assertNotReached (); +} + +void Label::drawImpl (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("drawImpl", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawWidgetBox (view, area, selected); + + if (getStyle ()) { + ensureStyles (); + + // Could adhere to style::textAlign. This can be used for + // centered text: + // + // int x = getAllocation()->x + // + (getAllocation()->width - totalSize.width) / 2; + // + // Now, only left-aligned text is supported. + + int x = getAllocation()->x + getStyle()->boxOffsetX (); + + // Same for style::valign: + // + // int baseY = getAllocation()->y + // + (getHeight () - (totalSize.ascent + totalSize.descent)) / 2 + // + totalSize.ascent; + + int baseY = + getAllocation()->y + totalSize.ascent + getStyle()->boxOffsetY (); + + for (int i = 0; i < words->size (); i++) { + Word *word = words->getRef(i); + view-> drawText (styles[(int)word->styleIndex]->font, + styles[(int)word->styleIndex]->color, + selected ? Color::SHADING_INVERSE + : Color::SHADING_NORMAL, + x, baseY, word->text, strlen (word->text)); + x += word->size.width; + } + } + + DBG_OBJ_LEAVE (); +} + +void Label::leaveNotifyImpl (EventCrossing *event) +{ + buttonDown = false; +} + +bool Label::buttonPressImpl (EventButton *event) +{ + if (link != -1 && event->button == 1) { + buttonDown = true; + return true; + } else + return false; +} + +bool Label::buttonReleaseImpl (EventButton *event) +{ + if (link != -1 && event->button == 1) { + if (buttonDown) + linkEmitter.emitClick (this, link, -1, -1, -1, event); + return true; + } else + return false; +} + +Iterator *Label::iterator (Content::Type mask, bool atEnd) +{ + return new LabelIterator (this, mask, atEnd); +} + +void Label::setStyle (Style *style) +{ + Widget::setStyle (style); + clearStyles (); +} + +void Label::select () +{ + selected = true; + queueDraw (); +} + +void Label::unselect () +{ + selected = false; + queueDraw (); +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/label.hh b/dwr/label.hh new file mode 100644 index 0000000..8a069b0 --- /dev/null +++ b/dwr/label.hh @@ -0,0 +1,87 @@ +#ifndef __DWR_LABEL_HH__ +#define __DWR_LABEL_HH__ + +#include "dwr/hideable.hh" +#include "lout/misc.hh" + +namespace rtfl { + +namespace dw { + +class Label: public Hideable +{ +private: + class LabelIterator: public ::dw::core::Iterator + { + private: + int index; + + LabelIterator (Label *label, ::dw::core::Content::Type mask, int index); + + public: + LabelIterator (Label *label, ::dw::core::Content::Type mask, bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, ::dw::core::HighlightLayer layer); + void unhighlight (int direction, ::dw::core::HighlightLayer layer); + void getAllocation (int start, int end, + ::dw::core::Allocation *allocation); + }; + + enum { ITALIC = 2, BOLD = 1 }; + + struct Word + { + char *text; + char styleIndex; + ::dw::core::Requisition size; + }; + + ::dw::core::style::Style *styles[4]; + lout::misc::SimpleVector<Word> *words; + ::dw::core::Requisition totalSize; + bool selected, buttonDown; + int link; + ::dw::core::Layout::LinkEmitter linkEmitter; + + void clearWords (); + void clearStyles (); + void ensureStyles (); + +protected: + void sizeRequestImplImpl (::dw::core::Requisition *requisition); + void getExtremesImplImpl (::dw::core::Extremes *extremes); + void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area); + + bool buttonPressImpl (::dw::core::EventButton *event); + bool buttonReleaseImpl (::dw::core::EventButton *event); + void leaveNotifyImpl (::dw::core::EventCrossing *event); + +public: + static int CLASS_ID; + + Label (const char *text, int link = -1); + ~Label (); + + void setText (const char *text); + inline void setLink (int link) { this->link = link; } + + ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd); + void setStyle (::dw::core::style::Style *style); + + void select (); + void unselect (); + + inline void connectLink (::dw::core::Layout::LinkReceiver *receiver) + { linkEmitter.connectLink (receiver); } +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_LABEL_HH__ diff --git a/dwr/toggle.cc b/dwr/toggle.cc new file mode 100644 index 0000000..a400237 --- /dev/null +++ b/dwr/toggle.cc @@ -0,0 +1,402 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "toggle.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int Toggle::CLASS_ID = -1; + +Toggle::ButtonStyle Toggle::buttonStyle = PLUS_MINUS; + +// ---------------------------------------------------------------------- + +Toggle::ToggleIterator::ToggleIterator (Toggle *toggle, Content::Type mask, + bool atEnd) : + Iterator (toggle, mask, atEnd) +{ + content.type = atEnd ? Content::END : Content::START; +} + +lout::object::Object *Toggle::ToggleIterator::clone () +{ + ToggleIterator *ti = + new ToggleIterator ((Toggle*)getWidget(), getMask(), false); + ti->content = content; + return ti; +} + +int Toggle::ToggleIterator::index () +{ + switch (content.type) { + case Content::START: + return 0; + case Content::WIDGET_IN_FLOW: + return content.widget == ((Toggle*)getWidget())->small ? 1 : 2; + case Content::END: + return 3; + default: + assertNotReached (); + return 0; + } +} + +int Toggle::ToggleIterator::compareTo (lout::object::Comparable *other) +{ + return index () - ((ToggleIterator*)other)->index (); +} + +bool Toggle::ToggleIterator::next () +{ + Toggle *toggle = (Toggle*)getWidget(); + + if (content.type == Content::END) + return false; + + // toggles only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::END; + return false; + } + + if (content.type == Content::START) { + if (toggle->small != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = toggle->small; + return true; + } else if (toggle->large != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = toggle->large; + return true; + } else { + content.type = Content::END; + return false; + } + } else /* if (content.type == Content::WIDGET) */ { + if (content.widget == toggle->small && toggle->large != NULL) { + content.widget = toggle->large; + return true; + } else { + content.type = Content::END; + return false; + } + } +} + +bool Toggle::ToggleIterator::prev () +{ + Toggle *toggle = (Toggle*)getWidget(); + + if (content.type == Content::START) + return false; + + // toggles only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::START; + return false; + } + + if (content.type == Content::END) { + if (toggle->large != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = toggle->large; + return true; + } else if (toggle->small != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = toggle->small; + return true; + } else { + content.type = Content::START; + return false; + } + } else /* if (content.type == Content::WIDGET) */ { + if (content.widget == toggle->large && toggle->small != NULL) { + content.widget = toggle->small; + return true; + } else { + content.type = Content::START; + return false; + } + } +} + +void Toggle::ToggleIterator::highlight (int start, int end, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Toggle::ToggleIterator::unhighlight (int direction, HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Toggle::ToggleIterator::getAllocation (int start, int end, + Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +// ---------------------------------------------------------------------- + +Toggle::Toggle (bool showLarge) +{ + DBG_OBJ_CREATE ("rtfl::dw::Toggle"); + registerName ("rtfl::dw::Toggle", &CLASS_ID); + + this->showLarge = showLarge; + small = large = NULL; + buttonDown = false; +} + +Toggle::~Toggle () +{ + if (small) + delete small; + if (large) + delete large; + + DBG_OBJ_DELETE (); +} + +void Toggle::sizeRequestImplImpl (Requisition *requisition) +{ + Widget *child = showLarge ? large : small; + Requisition childReq; + if (child) + child->sizeRequest (&childReq); + else + childReq.width = childReq.ascent = childReq.descent = 0; + + int buttonSize = calcButtonSize (); + requisition->width = + buttonSize + childReq.width + getStyle()->boxDiffWidth (); + requisition->ascent = + max (buttonSize, childReq.ascent) + getStyle()->boxOffsetY (); + requisition->descent = childReq.descent + getStyle()->boxRestHeight (); +} + + +void Toggle::getExtremesImplImpl (Extremes *extremes) +{ + Widget *child = showLarge ? large : small; + Extremes childExtr; + if (child) + child->getExtremes (&childExtr); + else + childExtr.minWidth = childExtr.maxWidth = 0; + + int buttonSize = calcButtonSize (); + extremes->minWidth = + buttonSize + childExtr.minWidth + getStyle()->boxDiffWidth (); + extremes->maxWidth = + buttonSize + childExtr.maxWidth + getStyle()->boxDiffWidth (); +} + + +void Toggle::sizeAllocateImpl (Allocation *allocation) +{ + Widget *visChild = showLarge ? large : small; + Widget *invisChild = showLarge ? small : large; + Allocation childAlloc; + int buttonSize = calcButtonSize (); + + if (visChild) { + childAlloc.x = allocation->x + buttonSize + getStyle()->boxOffsetX (); + childAlloc.y = allocation->y + getStyle()->boxOffsetY (); + childAlloc.width = + allocation->width - buttonSize - getStyle()->boxDiffWidth (); + childAlloc.ascent = allocation->ascent - getStyle()->boxOffsetY (); + childAlloc.descent = allocation->descent - getStyle()->boxRestHeight (); + visChild->sizeAllocate (&childAlloc); + } + + //printf ("toggle: visible child at (%d, %d), %d, (%d x %d)\n", + // childAlloc.x, childAlloc.y, childAlloc.width, + // childAlloc.ascent, childAlloc.descent); + + if (invisChild) { + // Zero size is used to hide a widget. + childAlloc.width = childAlloc.ascent = childAlloc.descent = 0; + invisChild->sizeAllocate (&childAlloc); + } +} + +void Toggle::drawImpl (View *view, Rectangle *area) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + + drawWidgetBox (view, area, false); + Widget *child = showLarge ? large : small; + Rectangle childArea; + if (child && child->intersects (area, &childArea)) + child->draw (view, &childArea); + + int buttonSize = calcButtonSize (); + int x0 = getAllocation()->x + getStyle()->boxOffsetX (); + int y0 = getAllocation()->y + getAllocation()->ascent - buttonSize; + + switch (buttonStyle) { + case PLUS_MINUS: + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + buttonSize / 2, + x0 + buttonSize - 2, y0 + buttonSize / 2); + if (!showLarge) + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + buttonSize / 2, y0 + 1, + x0 + buttonSize / 2, y0 + buttonSize - 2); + break; + + case TRIANGLE: + if (showLarge) { + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + 1, x0 + buttonSize - 2, y0 + 1); + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + 1, + x0 + buttonSize / 2, y0 + buttonSize - 2); + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + buttonSize - 2, y0 + 1, + x0 + buttonSize / 2, y0 + buttonSize - 2); + } else { + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + 1, x0 + 1, y0 + buttonSize - 2); + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + 1, + x0 + buttonSize - 2, y0 + buttonSize / 2); + view->drawLine (getStyle()->color, Color::SHADING_NORMAL, + x0 + 1, y0 + buttonSize - 2, + x0 + buttonSize - 2, y0 + buttonSize / 2); + } + break; + } + + DBG_OBJ_LEAVE (); +} + +::dw::core::Iterator *Toggle::iterator (Content::Type mask, bool atEnd) +{ + return new ToggleIterator (this, mask, atEnd); +} + +bool Toggle::insideButton (MousePositionEvent *event) +{ + int buttonSize = calcButtonSize (); + return + event->xWidget >= getStyle()->boxOffsetX () && + event->xWidget < getStyle()->boxOffsetX () + buttonSize && + event->yWidget >= getAllocation()->ascent - buttonSize && + event->yWidget < getAllocation()->ascent; +} + +bool Toggle::buttonPressImpl (EventButton *event) +{ + if (event->button == 1 && insideButton (event)) { + buttonDown = true; + return true; + } else + return false; +} + +bool Toggle::buttonReleaseImpl (EventButton *event) +{ + if (event->button == 1 && insideButton (event)) { + if (buttonDown) { + showLarge = !showLarge; + queueResize (0, true); + } + return true; + } else + return false; +} + +bool Toggle::motionNotifyImpl (::dw::core::EventMotion *event) +{ + if ((event->state & BUTTON1_MASK) && !insideButton (event)) + buttonDown = false; + return false; +} + +void Toggle::leaveNotifyImpl (EventCrossing *event) +{ + buttonDown = false; +} + +void Toggle::removeChild (Widget *child) +{ + if (child == small) + small = NULL; + else if (child == large) + large = NULL; + else + assertNotReached (); +} + +void Toggle::setSmall (Widget *widget) +{ + if (small) + delete small; + + small = widget; + if (small) + small->setParent (this); + + if (!showLarge) + queueResize (0, true); +} + +void Toggle::setLarge (Widget *widget) +{ + if (large) + delete large; + + large = widget; + if (large) + large->setParent (this); + + if (showLarge) + queueResize (0, true); +} + +void Toggle::toggle (bool showLarge) +{ + this->showLarge = showLarge; + queueResize (0, true); +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/toggle.hh b/dwr/toggle.hh new file mode 100644 index 0000000..8962ade --- /dev/null +++ b/dwr/toggle.hh @@ -0,0 +1,77 @@ +#ifndef __DWR_TOGGLE_HH__ +#define __DWR_TOGGLE_HH__ + +#include "hideable.hh" + +namespace rtfl { + +namespace dw { + +class Toggle: public Hideable +{ +private: + class ToggleIterator: public ::dw::core::Iterator + { + private: + int index (); + + public: + ToggleIterator (Toggle *toggle, ::dw::core::Content::Type mask, + bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, ::dw::core::HighlightLayer layer); + void unhighlight (int direction, ::dw::core::HighlightLayer layer); + void getAllocation (int start, int end, + ::dw::core::Allocation *allocation); + }; + + static enum ButtonStyle { PLUS_MINUS, TRIANGLE } buttonStyle; + + bool showLarge, buttonDown; + Widget *small, *large; + + bool insideButton (::dw::core::MousePositionEvent *event); + inline int calcButtonSize () + { // Always return an odd number. + int s = getStyle()->font->ascent; return (s % 2) ? s : s - 1; } + +protected: + void sizeRequestImplImpl (::dw::core::Requisition *requisition); + void getExtremesImplImpl (::dw::core::Extremes *extremes); + void sizeAllocateImpl (::dw::core::Allocation *allocation); + + void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area); + + bool buttonPressImpl (::dw::core::EventButton *event); + bool buttonReleaseImpl (::dw::core::EventButton *event); + bool motionNotifyImpl (::dw::core::EventMotion *event); + void leaveNotifyImpl (::dw::core::EventCrossing *event); + +public: + static int CLASS_ID; + + Toggle (bool showLarge); + ~Toggle (); + + ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd); + void removeChild (Widget *child); + + inline ::dw::core::Widget *getSmall () { return small; } + inline ::dw::core::Widget *getLarge () { return large; } + void setSmall (::dw::core::Widget *widget); + void setLarge (::dw::core::Widget *widget); + + inline bool isLargeShown () { return showLarge; } + void toggle (bool showLarge); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_TOGGLE_HH__ diff --git a/dwr/tools.cc b/dwr/tools.cc new file mode 100644 index 0000000..d45ebd3 --- /dev/null +++ b/dwr/tools.cc @@ -0,0 +1,243 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * ---------------------------------------------------------------------- + * + * A part of the code was written by Keith Vertanen and has been + * released to the public domain; see below. + */ + +#include "tools.hh" + +#include <math.h> + +using namespace dw::core; +using namespace dw::core::style; + +namespace rtfl { + +namespace dw { + +namespace tools { + +// Used for b-splines, see below. +struct point { + double x; + double y; + double z; +}; + +static void bspline (int n, int t, point *control, point *output, + int num_output); + +void drawArrowHead (View *view, Style *style, + int x1, int y1, int x2, int y2, int aheadlen) +{ + if (x1 != x2 || y1 != y2) { + // TODO: Use faster algorithm avoding floating point numbers. Also, + // regard that using integers could cause overflow errors. + + int l = sqrt ((double)(x2 - x1) * (double)(x2 - x1) + + (double)(y2 - y1) * (double)(y2 - y1)); + int x3 = (aheadlen * x1 + (l - aheadlen) * x2) / l; + int y3 = (aheadlen * y1 + (l - aheadlen) * y2) / l; + + int x4 = x3 - (y2 - y3) / 2; + int y4 = y3 + (x2 - x3) / 2; + view->drawLine (style->color, Color::SHADING_NORMAL, x2, y2, x4, y4); + + int x5 = x3 + (y2 - y3) / 2; + int y5 = y3 - (x2 - x3) / 2; + view->drawLine (style->color, Color::SHADING_NORMAL, x2, y2, x5, y5); + } +} + +void drawBSpline (::dw::core::View *view, ::dw::core::style::Style *style, + int degree, int numPoints, int *x, int *y) +{ + point *in = new point[numPoints]; + for (int i = 0; i < numPoints; i++) { + in[i].x = x[i]; + in[i].y = y[i]; + in[i].z = 0; + } + + int numOut = 5 * numPoints; + point *out = new point[numOut]; + + bspline(numPoints - 1, degree, in, out, numOut); + + for (int i = 0; i < numOut - 1; i++) + view->drawLine (style->color, ::dw::core::style::Color::SHADING_NORMAL, + out[i].x, out[i].y, + out[i + 1].x, out[i + 1].y); + + delete[] in; + delete[] out; +} + +/* ---------------------------------------------------------------------- + The following code was copied from + <ftp://ftp.grnet.gr/pub/lang/algorithms/c++/bspline.cpp>. + + It should be modified so that it is better adapted to our needs + (no z coordinate, no unnecessary conversion between int and + double, etc.) + ---------------------------------------------------------------------- */ + +/********************************************************************* + + Simple b-spline curve algorithm + + Copyright 1994 by Keith Vertanen (vertankd@cda.mrs.umn.edu) + + Released to the public domain (your mileage may vary) + +**********************************************************************/ + +static void compute_intervals(int *u, int n, int t); +static double blend(int k, int t, int *u, double v); +static void compute_point(int *u, int n, int t, double v, point *control, + point *output); + +void bspline(int n, int t, point *control, point *output, int num_output) + +/********************************************************************* + +Parameters: + n - the number of control points minus 1 + t - the degree of the polynomial plus 1 + control - control point array made up of point stucture + output - array in which the calculate spline points are to be put + num_output - how many points on the spline are to be calculated + +Pre-conditions: + n+2>t (no curve results if n+2<=t) + control array contains the number of points specified by n + output array is the proper size to hold num_output point structures + + +**********************************************************************/ + +{ + int *u; + double increment,interval; + point calcxyz; + int output_index; + + u=new int[n+t+1]; + compute_intervals(u, n, t); + + increment=(double) (n-t+2)/(num_output-1); // how much parameter goes up each time + interval=0; + + for (output_index=0; output_index<num_output-1; output_index++) + { + compute_point(u, n, t, interval, control, &calcxyz); + output[output_index].x = calcxyz.x; + output[output_index].y = calcxyz.y; + output[output_index].z = calcxyz.z; + interval=interval+increment; // increment our parameter + } + output[num_output-1].x=control[n].x; // put in the last point + output[num_output-1].y=control[n].y; + output[num_output-1].z=control[n].z; + + delete u; +} + +double blend(int k, int t, int *u, double v) // calculate the blending value +{ + double value; + + if (t==1) // base case for the recursion + { + if ((u[k]<=v) && (v<u[k+1])) + value=1; + else + value=0; + } + else + { + if ((u[k+t-1]==u[k]) && (u[k+t]==u[k+1])) // check for divide by zero + value = 0; + else + if (u[k+t-1]==u[k]) // if a term's denominator is zero,use just the other + value = (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v); + else + if (u[k+t]==u[k+1]) + value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v); + else + value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v) + + (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v); + } + return value; +} + +void compute_intervals(int *u, int n, int t) // figure out the knots +{ + int j; + + for (j=0; j<=n+t; j++) + { + if (j<t) + u[j]=0; + else + if ((t<=j) && (j<=n)) + u[j]=j-t+1; + else + if (j>n) + u[j]=n-t+2; // if n-t=-2 then we're screwed, everything goes to 0 + } +} + +void compute_point(int *u, int n, int t, double v, point *control, + point *output) +{ + int k; + double temp; + + // initialize the variables that will hold our outputted point + output->x=0; + output->y=0; + output->z=0; + + for (k=0; k<=n; k++) + { + temp = blend(k,t,u,v); // same blend is used for each dimension coordinate + output->x = output->x + (control[k]).x * temp; + output->y = output->y + (control[k]).y * temp; + output->z = output->z + (control[k]).z * temp; + } +} + +} // namespace tools + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/tools.hh b/dwr/tools.hh new file mode 100644 index 0000000..8e7bb2b --- /dev/null +++ b/dwr/tools.hh @@ -0,0 +1,24 @@ +#ifndef __DWR_TOOLS_HH__ +#define __DWR_TOOLS_HH__ + +#include "dw/core.hh" + +namespace rtfl { + +namespace dw { + +namespace tools { + +void drawArrowHead (::dw::core::View *view, ::dw::core::style::Style *style, + int x1, int y1, int x2, int y2, int aheadlen); + +void drawBSpline (::dw::core::View *view, ::dw::core::style::Style *style, + int degree, int numPoints, int *x, int *y); + +} // namespace tools + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_TOOLS_HH__ diff --git a/dwr/vbox.cc b/dwr/vbox.cc new file mode 100644 index 0000000..6649488 --- /dev/null +++ b/dwr/vbox.cc @@ -0,0 +1,164 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "vbox.hh" +#include "lout/misc.hh" + +using namespace dw::core; +using namespace lout::misc; + +namespace rtfl { + +namespace dw { + +int VBox::CLASS_ID = -1; + +VBox::VBox (bool stretchChildren): Box (stretchChildren) +{ + DBG_OBJ_CREATE ("rtfl::dw::VBox"); + registerName ("rtfl::dw::VBox", &CLASS_ID); +} + +VBox::~VBox () +{ + DBG_OBJ_DELETE (); +} + +int VBox::findFirstVisibleChild () +{ + for (int i = 0; i < children->size (); i++) { + Requisition childReq; + children->get(i)->sizeRequest (&childReq); + if (childReq.ascent > 0 || childReq.descent > 0) + return i; + } + + return 0; +} + +void VBox::sizeRequestImplImpl (Requisition *requisition) +{ + actualSizeRequestImplImpl (requisition, findFirstVisibleChild ()); +} + +void VBox::sizeAllocateImpl (Allocation *allocation) +{ + // TODO "stretchChildren" is not regarded. + + Allocation allocWOBorders; + allocWOBorders.x = allocation->x + getStyle()->boxOffsetX (); + allocWOBorders.y = allocation->y + getStyle()->boxOffsetY (); + allocWOBorders.width = allocation->width - getStyle()->boxDiffWidth (); + allocWOBorders.ascent = allocation->ascent - getStyle()->boxOffsetY (); + allocWOBorders.descent = allocation->descent - getStyle()->boxRestHeight (); + + // Unlike HBox, the ascent of the first visible widget (set as + // allocation ascent) is treated differently; the distribution is + // only done for the rest. Also, the difference is distributed to + // ascent (except first widget) and descent independently. + + int sumReqHeight = 0, firstVisibleChild = findFirstVisibleChild (); + for (int i = firstVisibleChild; i < children->size (); i++) { + Requisition childReq; + children->get(i)->sizeRequest (&childReq); + if (i > firstVisibleChild) + sumReqHeight += childReq.ascent; + sumReqHeight += childReq.descent; + } + + //bool stretch = stretchChildren && sumReqHeight < allocWOBorders.descent; + + // Cf. doc/rounding-errors.doc, with: x[i] = child requisition, + // y[i] = child allocation, a = total allocation, b = sumReqHeight. + int cumChildReqHeight = 0, cumChildAllocHeight = 0; + for (int i = 0; i < children->size (); i++) { + Widget *child = children->get (i); + + Requisition childReq; + child->sizeRequest (&childReq); + + Allocation childAlloc; + childAlloc.x = allocWOBorders.x; + childAlloc.width = allocWOBorders.width; + + if (i == firstVisibleChild) { + childAlloc.y = allocWOBorders.y; + childAlloc.ascent = allocWOBorders.ascent; + } else { + childAlloc.y = + allocWOBorders.y + allocWOBorders.ascent + cumChildAllocHeight; + cumChildReqHeight += childReq.ascent; + childAlloc.ascent = sumReqHeight > 0 ? + safeATimesBDividedByC (cumChildReqHeight, allocWOBorders.descent, + sumReqHeight) - cumChildAllocHeight : + 0; + cumChildAllocHeight += childAlloc.ascent; + } + + cumChildReqHeight += childReq.descent; + childAlloc.descent = sumReqHeight > 0 ? + safeATimesBDividedByC (cumChildReqHeight, allocWOBorders.descent, + sumReqHeight) - cumChildAllocHeight : + 0; + cumChildAllocHeight += childAlloc.descent; + + child->sizeAllocate (&childAlloc); + + //printf ("vbox: %dth child at (%d, %d), %d, (%d x %d)\n", + // i, childAlloc.x, childAlloc.y, childAlloc.width, + // childAlloc.ascent, childAlloc.descent); + + } +} + +void VBox::accumulateSize (int index, int size, Requisition *totalReq, + Requisition *childReq, int data1) +{ + int firstVisibleChild = data1; + + totalReq->width = max (totalReq->width, childReq->width); + if (index == firstVisibleChild) { + // Resulting baseline is the baseline of the first child. + totalReq->ascent = childReq->ascent; + totalReq->descent = childReq->descent; + } else + totalReq->descent += (childReq->ascent + childReq->descent); +} + + +void VBox::accumulateExtremes (int index, int size, Extremes *totalExtr, + Extremes *childExtr) +{ + totalExtr->minWidth = max (totalExtr->minWidth, childExtr->minWidth); + totalExtr->maxWidth = max (totalExtr->maxWidth, childExtr->maxWidth); +} + +} // namespace rtfl + +} // namespace dw diff --git a/dwr/vbox.hh b/dwr/vbox.hh new file mode 100644 index 0000000..b586217 --- /dev/null +++ b/dwr/vbox.hh @@ -0,0 +1,35 @@ +#ifndef __DWR_VBOX_HH__ +#define __DWR_VBOX_HH__ + +#include "box.hh" + +namespace rtfl { + +namespace dw { + +class VBox: public Box +{ +private: + int findFirstVisibleChild (); + +protected: + void sizeRequestImplImpl (::dw::core::Requisition *requisition); + void sizeAllocateImpl (::dw::core::Allocation *allocation); + void accumulateSize (int index, int size, ::dw::core::Requisition *totalReq, + ::dw::core::Requisition *childReq, int data1); + void accumulateExtremes (int index, int size, + ::dw::core::Extremes *totalExtr, + ::dw::core::Extremes *childExtr); + +public: + static int CLASS_ID; + + VBox (bool stretchChildren); + ~VBox (); +}; + +} // namespace rtfl + +} // namespace dw + +#endif // __DWR_VBOX_HH__ diff --git a/java/Hello.java b/java/Hello.java new file mode 100644 index 0000000..04eadd7 --- /dev/null +++ b/java/Hello.java @@ -0,0 +1,21 @@ +package rtfl; + +public class Hello +{ + int i1; + Integer i2; + String s1; + + public static void main (String[] args) + { + new Hello().hello(); + } + + public void hello() + { + System.out.println ("===== Hello Java! ====="); + i1 = 5; + i2 = new Integer(7); + s1 = "Hi!"; + } +} diff --git a/java/Makefile.am b/java/Makefile.am new file mode 100644 index 0000000..9d3f511 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,46 @@ +AM_CFLAGS = -Wall $(JAVA_CFLAGS) + +JAVA = $(JAVA_HOME)/jre/bin/java +JAVAC = $(JAVA_HOME)/bin/javac + +lib_LTLIBRARIES = librtfl-jvm-ti.la + +librtfl_jvm_ti_la_SOURCES = \ + main.c \ + class.h \ + class.c \ + method.h \ + method.c \ + field.h \ + field.c \ + config.h \ + config.c \ + misc.h \ + misc.c + +EXTRA_DIST = README Hello.java TestRtflObjects1.java + +# Run tests without installation. +LIBPATH=./.libs + +run-hello: $(LIBPATH)/librtfl-jvm-ti.so rtfl/Hello.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.Hello + +run-test-rtfl-objects-1: $(LIBPATH)/librtfl-jvm-ti.so rtfl/TestRtflObjects1.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.TestRtflObjects1 + +run-test-rtfl-objects-2: $(LIBPATH)/librtfl-jvm-ti.so rtfl/TestRtflObjects2.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.TestRtflObjects2 + +rtfl/Hello.class: Hello.java + $(JAVAC) -g -d . Hello.java + +rtfl/TestRtflObjects1.class: TestRtflObjects1.java + $(JAVAC) -g -d . TestRtflObjects1.java + +rtfl/TestRtflObjects2.class: TestRtflObjects2.java + $(JAVAC) -g -d . TestRtflObjects2.java + +clean-local: + find -name "*.class" | xargs rm -f + diff --git a/java/README b/java/README new file mode 100644 index 0000000..e0196a0 --- /dev/null +++ b/java/README @@ -0,0 +1,40 @@ +This sub-project aims to provide an agent for the Java Virtual +Machine, which uses the JVM Tool Interface (see [1] and [2]) to print +RTFL messages for Java programs, which must not necessarily be +prepared. This makes using RTFL much less time-consuming and so lowers +the entrance barrier. + +The agent consists of the file "librtfl-jvm-ti.so", which is passed to +"java" by the option "-agentlib:rtfl-jvm-ti"; see Makefile.am for +details. + +Run "make run-hello" or "run run-test-rtfl-objects-1" to run some +sample programs with the agent. + + +How it works +------------ +Several commands from the object module (like "create") follow the +program structure, and can so easily be printed when the respective +JVM-TI event is processed. Others (like "enter") can be printed by +this approach, but some parameters are missing (aspect and priority) +and so replaced by standard values (empty aspect and priority 0). It +is planned to make these parameters configurable, either by files or +by annotations. + +A third group of commands (like "msg") must still be added explicitly +to code. (For "msg", one could think of an integration with existing +logging frameworks.) + +Since Java programs often use many third-party code (libraries, +containers, ...), it is crucial for the performance to filter quite +early, and so reduce the total amount of messages. Filtering by +packages seems feasible (like "com.acme" and sub-packages when code of +ACME is debugged). Currently, only "rtfl" and its sub-packages are +considered; this is hard-coded in the function include_class() in +"config.c". + +---------------------------------------------------------------------- + +[1] http://docs.oracle.com/javase/8/docs/technotes/guides/jvmti/index.html +[2] http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html diff --git a/java/TestRtflObjects1.java b/java/TestRtflObjects1.java new file mode 100644 index 0000000..fece128 --- /dev/null +++ b/java/TestRtflObjects1.java @@ -0,0 +1,50 @@ +package rtfl; + +import java.util.LinkedList; +import java.util.List; + +public class TestRtflObjects1 +{ + private static class A + { + private A other = null; + private List<Integer> numbers = new LinkedList<Integer>(); + + public void setOther (A other) + { + this.other = other; + } + + public int doSomething (int n) + { + int r = (int)(Math.random() * 251); + numbers.add (new Integer (r)); + + if (other != null && n > 0) + other.doSomething (n - 1); + + return r; + } + } + + private static class B extends A + { + } + + private static class C extends A + { + } + + public static void main (String[] args) + { + A x = new A (); + B y = new B (); + C z = new C (); + + x.setOther (y); + y.setOther (z); + z.setOther (x); + + x.doSomething (8); + } +} diff --git a/java/class.c b/java/class.c new file mode 100644 index 0000000..d4e78ae --- /dev/null +++ b/java/class.c @@ -0,0 +1,39 @@ +#include "class.h" +#include "config.h" +#include "misc.h" + +void JNICALL class_prepare(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jclass klass) +{ + jvmtiError error; + char *class_sig = NULL, *class_name = NULL; + + if ((error = (*jvmti)->GetClassSignature (jvmti, klass, &class_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassSignature"); + else { + if ((class_name = get_class_name_from_sig (class_sig, TRUE)) && + include_class (class_name)) { + jint field_count; + jfieldID* fields; + if ((error = (*jvmti)->GetClassFields (jvmti, klass, &field_count, + &fields)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassFields"); + else { + int i; + for (i = 0; i < field_count; i++) { + if ((error = + (*jvmti)->SetFieldModificationWatch (jvmti, klass, + fields[i])) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "SetFieldModificationWatch"); + } + } + } + } + + jvmti_dealloc (jvmti, class_sig); + simple_free (class_name); + +} diff --git a/java/class.h b/java/class.h new file mode 100644 index 0000000..41ffb74 --- /dev/null +++ b/java/class.h @@ -0,0 +1,9 @@ +#ifndef __JAVA_CLASS_H__ +#define __JAVA_CLASS_H__ + +#include <jvmti.h> + +void JNICALL class_prepare(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jclass klass); + +#endif /* __JAVA_CLASS_H__ */ diff --git a/java/field.c b/java/field.c new file mode 100644 index 0000000..3427218 --- /dev/null +++ b/java/field.c @@ -0,0 +1,156 @@ +#include <string.h> +#include <stdlib.h> + +#include "field.h" +#include "config.h" +#include "misc.h" + +/* ---------------------------------------------------------------------- + + A sketch on how field modifications could be processed: + + A field modification results in a "set" or in an "assoc" + command. Neither is the field name *necessarily* the name of the "set" + command (or "assoc", if it would support names), nor is the field + value necessarily the respective value. Instead, the following rules + are used to determine the structure of the fields. + + (A) If the value type is "printable" (i. e. primitive, or a wrapper + class for a primitive type, or java.lang.String, or an enum (more + cases?)), then a set command is printed, with the name and the value + of the field. + + (B) If the value is a visible object, and it is not configured as + sub-structure (see below), an "assoc" command is printed, between this + object and the value object. (As soon as associations get a name, the + field name is used for this.) + + (C) If the value is an invisible object, or a visible object which is + configured as sub-structure, regard it as structured, according to the + rules which follow. + + Structured values are divided into sub-values with sub-names; the + field name, followed by a dot, is used as base-name; all sub-values + and sub-names are processed recursively according to the same rules, + while the base-name is eventually prepended to all sub-names. + + (C1) If the value is of type java.util.Map, and the keys are printable + (see (A)), use the keys as sub-names, the values as sub-values. ("The + keys are printable": does this mean that the type for the keys is a + class which is generally printable?) + + (C2) If the value is of type java.util.Map, and the keys are not + printable (see also C1), regard the value as unsorted list of + elements, which are themselves structured sub-values + (java.util.Map.Entry?). + + (C3) If the value is of type java.util.List, use the indices as + sub-names, and the elements as sub-values. + + (C4) If the value is an unsorted collection, i. e. of type + java.util.Collection, but not of java.util.Map or java.util.List, + apply a random order and proceed as in (C4). (Unsorted collections are + not really supported in RTFL.) + + The rules (C1) to (C4) apply not when configured otherwise ("do not + show logical structure"). + + (C5) Otherwise (not of type java.util.Collection or java.util.Map), + examine fields of the value; regard the names of its fields as + sub-names; their values as sub-values; + + (Alternative: Java Beans?) + + For simplification, ignore C1 to C4 in the first place and focus on + C5; then implement C1 to C4 by and by. + + ---------------------------------------------------------------------- */ + +static void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass, + jobject object, jfieldID field, jvalue value, + char *base_name); +static void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str, + char *base_name, char *field_name, + jstring string); + +void JNICALL field_modification (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jlocation location, + jclass field_klass, jobject object, + jfieldID field, char signature_type, + jvalue new_value) +{ + // The class name is irrelevant here, since classes are already + // filtered inclass_prepare(), where SetFieldModificationWatch() + // is called. + + print_field (jvmti, jni, field_klass, object, field, new_value, ""); +} + +void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass, + jobject object, jfieldID field, jvalue value, char *base_name) +{ + jvmtiError error; + char object_buf1[SIZE_OBJECT_BUF], object_buf2[SIZE_OBJECT_BUF]; + char *class_name = NULL, *field_name = NULL, *field_sig = NULL; + + if ((error = (*jvmti)->GetFieldName (jvmti, field_klass, field, &field_name, + &field_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetFieldName"); + else { + //printf ("==> %s - %s\n", field_name, field_sig); + + fill_object_buf (jni, object_buf1, object); + + if ((class_name = get_class_name_from_sig (field_sig, FALSE))) { + if (include_class (class_name)) { + // The field represents an instance of a class which is also + // included: this is an association, not a simple field. + // ("field_name" could be used here, as soon as associations get + // a name.) + + if (value.l) { + fill_object_buf (jni, object_buf2, value.l); + RTFL_OBJ_PRINT ("assoc", "s:s", object_buf1, object_buf2); + } + } else { + if (strcmp (class_name, "java.lang.String")) { + print_string_field (jvmti, jni, object_buf1, base_name, + field_name, (jstring)(value.l)); + + } + } + } + } + + jvmti_dealloc (jvmti, field_name); + jvmti_dealloc (jvmti, field_sig); + simple_free (class_name); +} + +void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str, + char *base_name, char *field_name, jstring string) +{ + if (string == NULL) + RTFL_OBJ_PRINT ("set", "s:ss:s", object_str, base_name, field_name, + "null"); + else { + // TODO: JNI calls somehow cause an abortion. Just some test + // code. + + //jsize len = (*jni)->GetStringLength (jni, string); + //const jchar *chars = (*jni)->GetStringChars (jni, string, NULL); + + //char *chars0 = (char*)malloc (len + 1); + //memcpy (chars0, chars, len); + //chars0[len] = 0; + + char *chars0 = "?\"?\"?"; + + RTFL_OBJ_PRINT ("set", "s:ss:\"q\"", object_str, base_name, field_name, + chars0); + + //free (chars0); + //(*jni)->ReleaseStringChars (jni, string, chars); + } +} diff --git a/java/field.h b/java/field.h new file mode 100644 index 0000000..6d580ee --- /dev/null +++ b/java/field.h @@ -0,0 +1,12 @@ +#ifndef __JAVA_FIELD_H__ +#define __JAVA_FIELD_H__ + +#include <jvmti.h> + +void JNICALL field_modification (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jlocation location, + jclass field_klass, jobject object, + jfieldID field, char signature_type, + jvalue new_value); + +#endif /* __JAVA_FIELD_H__ */ diff --git a/java/main.c b/java/main.c new file mode 100644 index 0000000..dc7e954 --- /dev/null +++ b/java/main.c @@ -0,0 +1,68 @@ +#include <jvmti.h> +#include <string.h> +#include <stdlib.h> + +#include "class.h" +#include "method.h" +#include "field.h" +#include "misc.h" + +JNIEXPORT jint JNICALL Agent_OnLoad (JavaVM *jvm, char *options, void *reserved) +{ + rtfl_print ("obj", RTFL_OBJ_VERSION, "", 0, "s", "noident"); + + jvmtiEnv *jvmti = NULL; + + (*jvm)->GetEnv (jvm, (void**)&jvmti, JVMTI_VERSION_1_0); + + jvmtiCapabilities capa; + memset (&capa, 0, sizeof(jvmtiCapabilities)); + capa.can_generate_method_entry_events = 1; + capa.can_generate_method_exit_events = 1; + capa.can_access_local_variables = 1; + capa.can_generate_field_modification_events = 1; + + jvmtiEventCallbacks callbacks; + (void)memset(&callbacks, 0, sizeof(callbacks)); + callbacks.ClassPrepare = &class_prepare; + callbacks.MethodEntry = &method_entry; + callbacks.MethodExit = &method_exit; + callbacks.FieldModification = &field_modification; + + jvmtiError error; + if ((error = (*jvmti)->AddCapabilities(jvmti, &capa)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "AddCapabilities"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_CLASS_PREPARE, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_CLASS_PREPARE)"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_METHOD_ENTRY, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_METHOD_ENTRY)"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_METHOD_EXIT, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_METHOD_EXIT)"); + else if ((error = + (*jvmti)->SetEventNotificationMode + (jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, + (jthread)NULL)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_FIELD_MODIFICATION"); + else if ((error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, + (jint)sizeof(callbacks))) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "SetEventCallbacks"); + + return JNI_OK; +} diff --git a/java/method.c b/java/method.c new file mode 100644 index 0000000..35e6022 --- /dev/null +++ b/java/method.c @@ -0,0 +1,111 @@ +#include <string.h> + +#include "method.h" +#include "config.h" +#include "misc.h" + +static bool handle_method (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, char **object, char **method_name, + char **class_name) +{ + jvmtiError error; + char *class_sig = NULL, object_buf[SIZE_OBJECT_BUF]; + jclass klass; + bool success = FALSE; + + *object = *method_name = *class_name = NULL; + + if ((error = + (*jvmti)->GetMethodName (jvmti, method, method_name, NULL, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetMethodName"); + else if ((error = + (*jvmti)->GetMethodDeclaringClass (jvmti, method, &klass)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetMethodDeclaringClass"); + else if ((error = + (*jvmti)->GetClassSignature (jvmti, klass, &class_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassSignature"); + else { + if ((*class_name = get_class_name_from_sig (class_sig, TRUE)) && + include_class (*class_name)) { + jint numlocals; + jvmtiLocalVariableEntry *locals; + if ((error = (*jvmti)->GetLocalVariableTable (jvmti, method, + &numlocals, &locals)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetLocalVariableTable"); + else { + jint j, this_slot = -1; + for (j = 0; j < numlocals && this_slot == -1; j++) + if (strcmp (locals[j].name, "this") == 0) + this_slot = locals[j].slot; + + jobject this_obj; + + if (this_slot == -1) { + this_obj = NULL; + success = TRUE; + } else { + if ((error = (*jvmti)->GetLocalObject (jvmti, thread, 0, + this_slot, &this_obj)) + != JVMTI_ERROR_NONE) { + jvmti_error (jvmti, error, "GetLocalObject"); + } else + success = TRUE; + } + + if (success) { + fill_object_buf (jni, object_buf, this_obj); + *object = strdup (object_buf); + } + } + } + } + + jvmti_dealloc (jvmti, class_sig); + + return success; +} + +static void handle_method_free (jvmtiEnv *jvmti, char *object, + char *method_name, char *class_name) +{ + simple_free (object); + jvmti_dealloc (jvmti, method_name); + simple_free (class_name); +} + +void JNICALL method_entry (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method) +{ + char *object, *method_name, *class_name; + + if (handle_method (jvmti, jni, thread, method, &object, &method_name, + &class_name)) { + if (strcmp (method_name, "<init>") == 0) + RTFL_OBJ_PRINT ("create", "s:s", object, class_name); + else + RTFL_OBJ_PRINT ("enter", "s:s:d:s:", + object, "", 0, method_name, class_name); + } + + handle_method_free (jvmti, object, method_name, class_name); +} + +void JNICALL method_exit (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jboolean was_popped_by_exception, + jvalue return_value) +{ + char *object, *method_name, *class_name; + + if (handle_method (jvmti, jni, thread, method, &object, &method_name, + &class_name)) { + if (strcmp (method_name, "<init>") != 0) + // Hopefully, this is the correct method. + RTFL_OBJ_PRINT ("leave", "s", object); + } + + handle_method_free (jvmti, object, method_name, class_name); +} diff --git a/java/method.h b/java/method.h new file mode 100644 index 0000000..e3dfc45 --- /dev/null +++ b/java/method.h @@ -0,0 +1,12 @@ +#ifndef __JAVA_METHOD_H__ +#define __JAVA_METHOD_H__ + +#include <jvmti.h> + +void JNICALL method_entry (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method); +void JNICALL method_exit (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jboolean was_popped_by_exception, + jvalue return_value); + +#endif /* __JAVA_METHOD_H__ */ diff --git a/java/misc.c b/java/misc.c new file mode 100644 index 0000000..09d0668 --- /dev/null +++ b/java/misc.c @@ -0,0 +1,191 @@ +#include <string.h> +#include <stdlib.h> + +#include "misc.h" + +bool str_starts_with (const char *haystack, const char *needle) +{ + int haystack_len = strlen (haystack), needle_len = strlen (needle); + if (haystack_len < needle_len) + return FALSE; + else + return memcmp (haystack, needle, needle_len * sizeof (char)) == 0; +} + +void jvmti_error (jvmtiEnv *jvmti, jvmtiError error, const char *fmt, ...) +{ + char *errorname; + if ((*jvmti)->GetErrorName (jvmti, error, &errorname) != JVMTI_ERROR_NONE) + errorname = "<error getting error name>"; + + fprintf(stderr, "error (jvmtiError %d: %s): ", error, errorname); + + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + + fprintf(stderr, "\n"); +} + +void other_error (const char *fmt, ...) +{ + fprintf(stderr, "error (other): "); + + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + + fprintf(stderr, "\n"); +} + +void jvmti_dealloc (jvmtiEnv *jvmti, void *mem) +{ + if (mem) { + jvmtiError error; + if ((error = (*jvmti)->Deallocate (jvmti, mem)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "Deallocate"); + } +} + +void simple_free (void *mem) +{ + if (mem) + free (mem); +} + +char *get_class_name_from_sig (const char *class_sig, bool expect_class) +{ + int len_class_sig = strlen (class_sig); + if (!(class_sig[0] == 'L' && class_sig[len_class_sig - 1] == ';')) { + if (expect_class) + other_error ("don't know how to deal with class signature '%s'", + class_sig); + return NULL; + } else { + char *class_name = + (char*)malloc ((len_class_sig - 2 + 1) * sizeof (char)); + int i; + for (i = 0; i < len_class_sig - 2; i++) + class_name[i] = class_sig[i + 1] == '/' ? '.' : class_sig[i + 1]; + class_name[len_class_sig - 2] = 0; + return class_name; + } +} + +/* ------------------------------------------------------------------- + + The only way to identify objects seems to be the JNI method + IsSameObject; although jobject is a pointer, the equality of two + jobject's does not imply the identity of the represented Java + objects. + + Furthermore, since we only have one object at hand to get an + identifier needed for RTFL messages, we store all objects in a + list, after creating a global reference (NewGlobalRef); the index + in the list then identifies the object. This foils the garbage + collection, but for short debugging sessions, this should be + acceptable. + + ---------------------------------------------------------------------- */ + +static size_t reg_objects_size = 0, reg_objects_alloc_size; +static jobject *reg_objects = NULL; + +size_t object_index (JNIEnv* jni, jobject object) +{ + size_t i; + for (i = 0; i < reg_objects_size; i++) + if ((*jni)->IsSameObject (jni, object, reg_objects[i])) + return i; + + reg_objects_size++; + if (reg_objects == NULL) { + reg_objects_alloc_size = 1; + reg_objects = + (jobject*)malloc (reg_objects_alloc_size * sizeof (jobject)); + } else { + reg_objects_alloc_size <<= 1; + reg_objects = + (jobject*)realloc (reg_objects, + reg_objects_alloc_size * sizeof (jobject)); + } + + i = reg_objects_size - 1; + reg_objects[i] = (*jni)->NewGlobalRef (jni, object); + return i; +} + +void fill_object_buf (JNIEnv* jni, char *object_buf, jobject object) +{ + if (object) + snprintf (object_buf, SIZE_OBJECT_BUF, "%ld", object_index (jni, object)); + else + strcpy (object_buf, "null"); +} + +// Copied from "debug_rtfl.hh". +void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...) +{ + // "\n" at the beginning just in case that the previous line is not + // finished yet. + printf ("\n[rtfl-%s-%s]%s:%d:pid(todo):", module, version, file, line); + + va_list args; + va_start (args, fmt); + + int i; + for (i = 0; fmt[i]; i++) { + int n, j; + void *p; + char *s; + + switch (fmt[i]) { + case 'd': + n = va_arg(args, int); + printf ("%d", n); + break; + + case 'p': + p = va_arg(args, void*); + printf ("%p", p); + break; + + case 's': + s = va_arg (args, char*); + for (j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + putchar (s[j]); + } + break; + + case 'q': + s = va_arg (args, char*); + for (j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + else if (s[j] == '\"') + printf ("\\\\"); // a quoted quoting character + putchar (s[j]); + } + break; + + case 'c': + n = va_arg(args, int); + printf ("#%06x", n); + break; + + default: + putchar (fmt[i]); + break; + } + } + + va_end (args); + + putchar ('\n'); + fflush (stdout); +} diff --git a/java/misc.h b/java/misc.h new file mode 100644 index 0000000..e88879e --- /dev/null +++ b/java/misc.h @@ -0,0 +1,41 @@ +#ifndef __JAVA_MISC_H__ +#define __JAVA_MISC_H__ + +#include <jvmti.h> + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef __GNUC__ +# define __attribute__(x) /* nothing */ +#endif + +typedef enum { FALSE = 0, TRUE = 1 } bool; +enum { SIZE_OBJECT_BUF = 32 }; + +bool str_starts_with (const char *haystack, const char *needle); + +void jvmti_error (jvmtiEnv *jvmti, jvmtiError error, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +void other_error (const char *fmt, ...) __attribute__((format(printf, 1, 2))); + +void jvmti_dealloc (jvmtiEnv *jvmti, void *mem); +void simple_free (void *mem); + +char *get_class_name_from_sig (const char *class_sig, bool expect_class); + +size_t object_index (JNIEnv* jni, jobject object); +void fill_object_buf (JNIEnv* jni, char *object_buf, jobject object); + +void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...); + +#define RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, "", 1, "s:" fmt, cmd, __VA_ARGS__) + +#define RTFL_OBJ_VERSION "1.0" + +#define RTFL_OBJ_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#endif /* __JAVA_MISC_H__ */ diff --git a/lout/Makefile.am b/lout/Makefile.am new file mode 100644 index 0000000..f221936 --- /dev/null +++ b/lout/Makefile.am @@ -0,0 +1,21 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/lout"' + +noinst_LIBRARIES = liblout.a + +liblout_a_SOURCES = \ + container.cc \ + container.hh \ + debug.hh \ + identity.cc \ + identity.hh \ + misc.cc \ + misc.hh \ + object.cc \ + object.hh \ + signal.cc \ + signal.hh \ + unicode.cc \ + unicode.hh \ + msg.h diff --git a/lout/container.cc b/lout/container.cc new file mode 100644 index 0000000..9ed14d8 --- /dev/null +++ b/lout/container.cc @@ -0,0 +1,813 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define PRINTF(fmt, ...) +//#define PRINTF printf + +#include "container.hh" +#include "misc.hh" +#include "debug.hh" + +namespace lout { + +using namespace object; + +namespace container { + +namespace untyped { + +// ------------- +// Iterator +// ------------- + +Iterator::Iterator() +{ + impl = NULL; +} + +Iterator::Iterator(const Iterator &it2) +{ + impl = it2.impl; + if (impl) + impl->ref(); +} + +Iterator::Iterator(Iterator &it2) +{ + impl = it2.impl; + if (impl) + impl->ref(); +} + +Iterator &Iterator::operator=(const Iterator &it2) +{ + if (impl) + impl->unref(); + impl = it2.impl; + if (impl) + impl->ref(); + return *this; +} + +Iterator &Iterator::operator=(Iterator &it2) +{ + if (impl) + impl->unref(); + impl = it2.impl; + if (impl) + impl->ref(); + return *this; +} + +Iterator::~Iterator() +{ + if (impl) + impl->unref(); +} + +// ---------------- +// Collection +// ---------------- + +void Collection::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("{ "); + bool first = true; + for (Iterator it = iterator(); it.hasNext(); ) { + if (!first) + sb->append(", "); + it.getNext()->intoStringBuffer(sb); + first = false; + } + sb->append(" }"); +} + +// ------------ +// Vector +// ------------ + +Vector::Vector(int initSize, bool ownerOfObjects) +{ + DBG_OBJ_CREATE ("lout::container::untyped::Vector"); + + numAlloc = initSize == 0 ? 1 : initSize; + this->ownerOfObjects = ownerOfObjects; + numElements = 0; + array = (Object**)malloc(numAlloc * sizeof(Object*)); +} + +Vector::~Vector() +{ + clear(); + free(array); + + DBG_OBJ_DELETE (); +} + +int Vector::size () +{ + return numElements; +} + +void Vector::put(Object *newElement, int newPos) +{ + if (newPos == -1) + newPos = numElements; + + // Old entry is overwritten. + if (newPos < numElements) { + if (ownerOfObjects && array[newPos]) + delete array[newPos]; + } + + // Allocated memory has to be increased. + if (newPos >= numAlloc) { + while (newPos >= numAlloc) + numAlloc *= 2; + array = (Object**)realloc(array, numAlloc * sizeof(Object*)); + } + + // Insert NULL's into possible gap before new position. + for (int i = numElements; i < newPos; i++) + array[i] = NULL; + + if (newPos >= numElements) + numElements = newPos + 1; + + array[newPos] = newElement; +} + +void Vector::clear() +{ + if (ownerOfObjects) { + for (int i = 0; i < numElements; i++) + if (array[i]) + delete array[i]; + } + + numElements = 0; +} + +void Vector::insert(Object *newElement, int pos) +{ + if (pos >= numElements) + put(newElement, pos); + else { + numElements++; + + // Allocated memory has to be increased. + if (numElements >= numAlloc) { + numAlloc *= 2; + array = (Object**)realloc(array, numAlloc * sizeof(Object*)); + } + + for (int i = numElements - 1; i > pos; i--) + array[i] = array[i - 1]; + + array[pos] = newElement; + } +} + +void Vector::remove(int pos) +{ + if (ownerOfObjects && array[pos]) + delete array[pos]; + + for (int i = pos + 1; i < numElements; i++) + array[i - 1] = array[i]; + + numElements--; +} + +/** + * Sort the elements in the vector. Assumes that all elements are Comparable's. + */ +void Vector::sort(Comparator *comparator) +{ + Comparator::compareFunComparator = comparator; + qsort (array, numElements, sizeof(Object*), Comparator::compareFun); +} + +/** + * \brief Use binary search to find an element in a sorted vector. + * + * If "mustExist" is true, only exact matches are found; otherwise, -1 + * is returned. If it is false, the position of the next greater + * element is returned, or, if the key is the greatest element, the + * size of the array. (This is the value which can be used for + * insertion; see insertSortet()). + */ +int Vector::bsearch(Object *key, bool mustExist, int start, int end, + Comparator *comparator) +{ + // The case !mustExist is not handled by bsearch(3), so here is a + // new implementation. + + DBG_OBJ_MSGF ("container", 0, + "<b>bsearch</b> (<i>key</i>, %s, %d, %d, <i>comparator</i>) " + "[size is %d]", + mustExist ? "true" : "false", start, end, size ()); + DBG_OBJ_MSG_START (); + + int result = -123; // Compiler happiness: GCC 4.7 does not handle this? + + if (start > end) { + DBG_OBJ_MSG ("container", 1, "empty"); + result = mustExist ? -1 : start; + } else { + int low = start, high = end; + bool found = false; + + while (!found) { + int index = (low + high) / 2; + int c = comparator->compare (key, array[index]); + DBG_OBJ_MSGF ("container", 1, + "searching within %d and %d; compare key with #%d => %d", + low, high, index, c); + if (c == 0) { + found = true; + result = index; + } else { + if (low >= high) { + if (mustExist) { + found = true; + result = -1; + } else { + found = true; + result = c > 0 ? index + 1 : index; + } + } + + if (c < 0) + high = index - 1; + else + low = index + 1; + } + } + } + + DBG_OBJ_MSGF ("container", 1, "result = %d", result); + DBG_OBJ_MSG_END (); + return result; +} + +Object *Vector::VectorIterator::getNext() +{ + if (index < vector->numElements - 1) + index++; + + return index < vector->numElements ? vector->array[index] : NULL; +} + +bool Vector::VectorIterator::hasNext() +{ + return index < vector->numElements - 1; +} + +Collection0::AbstractIterator* Vector::createIterator() +{ + return new VectorIterator(this); +} + +// ------------ +// List +// ------------ + +List::List(bool ownerOfObjects) +{ + this->ownerOfObjects = ownerOfObjects; + first = last = NULL; + numElements = 0; +} + +List::~List() +{ + clear(); +} + +int List::size () +{ + return numElements; +} + +bool List::equals(Object *other) +{ + List *otherList = (List*)other; + Node *node1 = first, *node2 = otherList->first; + while (node1 != NULL && node2 != NULL ) { + if (!node1->object->equals (node2->object)) + return false; + node1 = node1->next; + node2 = node2->next; + } + return node1 == NULL && node2 == NULL; +} + +int List::hashValue() +{ + int h = 0; + for (Node *node = first; node; node = node->next) + h = h ^ node->object->hashValue (); + return h; +} + +void List::clear() +{ + while (first) { + if (ownerOfObjects && first->object) + delete first->object; + Node *next = first->next; + delete first; + first = next; + } + + last = NULL; + numElements = 0; +} + +void List::append(Object *element) +{ + Node *newLast = new Node; + newLast->next = NULL; + newLast->object = element; + + if (last) { + last->next = newLast; + last = newLast; + } else + first = last = newLast; + + numElements++; +} + +bool List::insertBefore(object::Object *beforeThis, object::Object *neew) +{ + Node *beforeCur, *cur; + + for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) { + if (cur->object == beforeThis) { + Node *newNode = new Node; + newNode->next = cur; + newNode->object = neew; + + if (beforeCur) + beforeCur->next = newNode; + else + first = newNode; + + numElements++; + return true; + } + } + + return false; +} + +bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll) +{ + Node *beforeCur, *cur; + + for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) { + if (compare ? + (cur->object && element->equals(cur->object)) : + element == cur->object) { + if (beforeCur) { + beforeCur->next = cur->next; + if (cur->next == NULL) + last = beforeCur; + } else { + first = cur->next; + if (first == NULL) + last = NULL; + } + + if (ownerOfObjects && cur->object && !doNotDeleteAtAll) + delete cur->object; + delete cur; + + numElements--; + return true; + } + } + + return false; +} + +Object *List::ListIterator::getNext() +{ + Object *object; + + if (current) { + object = current->object; + current = current->next; + } else + object = NULL; + + return object; +} + +bool List::ListIterator::hasNext() +{ + return current != NULL; +} + +Collection0::AbstractIterator* List::createIterator() +{ + return new ListIterator(first); +} + + +// --------------- +// HashSet +// --------------- + +HashSet::HashSet(bool ownerOfObjects, int tableSize) +{ + this->ownerOfObjects = ownerOfObjects; + this->tableSize = tableSize; + + table = new Node*[tableSize]; + for (int i = 0; i < tableSize; i++) + table[i] = NULL; + + numElements = 0; +} + +HashSet::~HashSet() +{ + for (int i = 0; i < tableSize; i++) { + Node *n1 = table[i]; + while (n1) { + Node *n2 = n1->next; + + // It seems appropriate to call "clearNode(n1)" here instead + // of "delete n1->object", but since this is the destructor, + // the implementation of a sub class would not be called + // anymore. This is the reason why HashTable has an + // destructor. + if (ownerOfObjects) { + PRINTF ("- deleting object: %s\n", n1->object->toString()); + delete n1->object; + } + + delete n1; + n1 = n2; + } + } + + delete[] table; +} + +int HashSet::size () +{ + return numElements; +} + +HashSet::Node *HashSet::createNode() +{ + return new Node; +} + +void HashSet::clearNode(HashSet::Node *node) +{ + if (ownerOfObjects) { + PRINTF ("- deleting object: %s\n", node->object->toString()); + delete node->object; + } +} + +HashSet::Node *HashSet::findNode(Object *object) const +{ + int h = calcHashValue(object); + for (Node *node = table[h]; node; node = node->next) { + if (object->equals(node->object)) + return node; + } + + return NULL; +} + +HashSet::Node *HashSet::insertNode(Object *object) +{ + // Look whether object is already contained. + Node *node = findNode(object); + if (node) { + clearNode(node); + numElements--; + } else { + int h = calcHashValue(object); + node = createNode (); + node->next = table[h]; + table[h] = node; + numElements++; + } + + node->object = object; + return node; +} + + +void HashSet::put(Object *object) +{ + insertNode (object); +} + +bool HashSet::contains(Object *object) const +{ + int h = calcHashValue(object); + for (Node *n = table[h]; n; n = n->next) { + if (object->equals(n->object)) + return true; + } + + return false; +} + +bool HashSet::remove(Object *object) +{ + int h = calcHashValue(object); + Node *last, *cur; + + for (last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) { + if (object->equals(cur->object)) { + if (last) + last->next = cur->next; + else + table[h] = cur->next; + + clearNode (cur); + delete cur; + numElements--; + + return true; + } + } + + return false; + + // TODO for HashTable: also remove value. +} + +// For historical reasons: this method once existed under the name +// "getKey" in HashTable. It could be used to get the real object from +// the table, but it was dangerous, because a change of this object +// would also change the hash value, and so confuse the table. + +/*Object *HashSet::getReference (Object *object) +{ + int h = calcHashValue(object); + for (Node *n = table[h]; n; n = n->next) { + if (object->equals(n->object)) + return n->object; + } + + return NULL; +}*/ + +HashSet::HashSetIterator::HashSetIterator(HashSet *set) +{ + this->set = set; + node = NULL; + pos = -1; + gotoNext(); +} + +void HashSet::HashSetIterator::gotoNext() +{ + if (node) + node = node->next; + + while (node == NULL) { + if (pos >= set->tableSize - 1) + return; + pos++; + node = set->table[pos]; + } +} + + +Object *HashSet::HashSetIterator::getNext() +{ + Object *result; + if (node) + result = node->object; + else + result = NULL; + + gotoNext(); + return result; +} + +bool HashSet::HashSetIterator::hasNext() +{ + return node != NULL; +} + +Collection0::AbstractIterator* HashSet::createIterator() +{ + return new HashSetIterator(this); +} + +// --------------- +// HashTable +// --------------- + +HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize) : + HashSet (ownerOfKeys, tableSize) +{ + this->ownerOfValues = ownerOfValues; +} + +HashTable::~HashTable() +{ + // See comment in the destructor of HashSet. + for (int i = 0; i < tableSize; i++) { + for (Node *n = table[i]; n; n = n->next) { + if (ownerOfValues) { + Object *value = ((KeyValuePair*)n)->value; + if (value) { + PRINTF ("- deleting value: %s\n", value->toString()); + delete value; + } + } + } + } +} + +HashSet::Node *HashTable::createNode() +{ + return new KeyValuePair; +} + +void HashTable::clearNode(HashSet::Node *node) +{ + HashSet::clearNode (node); + if (ownerOfValues) { + Object *value = ((KeyValuePair*)node)->value; + if (value) { + PRINTF ("- deleting value: %s\n", value->toString()); + delete value; + } + } +} + +void HashTable::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("{ "); + + bool first = true; + for (int i = 0; i < tableSize; i++) { + for (Node *node = table[i]; node; node = node->next) { + if (!first) + sb->append(", "); + node->object->intoStringBuffer(sb); + + sb->append(" => "); + + Object *value = ((KeyValuePair*)node)->value; + if (value) + value->intoStringBuffer(sb); + else + sb->append ("null"); + + first = false; + } + } + + sb->append(" }"); +} + +void HashTable::put(Object *key, Object *value) +{ + KeyValuePair *node = (KeyValuePair*)insertNode(key); + node->value = value; +} + +Object *HashTable::get(Object *key) const +{ + Node *node = findNode(key); + if (node) + return ((KeyValuePair*)node)->value; + else + return NULL; +} + +// ----------- +// Stack +// ----------- + +Stack::Stack (bool ownerOfObjects) +{ + this->ownerOfObjects = ownerOfObjects; + bottom = top = NULL; + numElements = 0; +} + +Stack::~Stack() +{ + while (top) + pop (); +} + +int Stack::size () +{ + return numElements; +} + +void Stack::push (object::Object *object) +{ + Node *newTop = new Node (); + newTop->object = object; + newTop->prev = top; + + top = newTop; + if (bottom == NULL) + bottom = top; + + numElements++; +} + +void Stack::pushUnder (object::Object *object) +{ + Node *newBottom = new Node (); + newBottom->object = object; + newBottom->prev = NULL; + if (bottom != NULL) + bottom->prev = newBottom; + + bottom = newBottom; + if (top == NULL) + top = bottom; + + numElements++; +} + +void Stack::pop () +{ + Node *newTop = top->prev; + + if (ownerOfObjects) + delete top->object; + delete top; + + top = newTop; + if (top == NULL) + bottom = NULL; + + numElements--; +} + +Object *Stack::StackIterator::getNext() +{ + Object *object; + + if (current) { + object = current->object; + current = current->prev; + } else + object = NULL; + + return object; +} + +bool Stack::StackIterator::hasNext() +{ + return current != NULL; +} + +Collection0::AbstractIterator* Stack::createIterator() +{ + return new StackIterator(top); +} + +} // namespace untyped + +} // namespace container + +} // namespace lout diff --git a/lout/container.hh b/lout/container.hh new file mode 100644 index 0000000..3051970 --- /dev/null +++ b/lout/container.hh @@ -0,0 +1,551 @@ +#ifndef __LOUT_CONTAINER_HH_ +#define __LOUT_CONTAINER_HH_ + +#include "object.hh" + +namespace lout { + +/** + * \brief This namespace contains a framework for container classes, which + * members are instances of object::Object. + * + * A common problem in languages without garbage collection is, where the + * children belong to, and so, who is responsible to delete them (instantiation + * is always done by the caller). This information is here told to the + * collections, each container has a constructor with the parameter + * "ownerOfObjects" (HashTable has two such parameters, for keys and values). + * + * \sa container::untyped, container::typed + */ +namespace container { + +/** + * \brief The container classes defined here contain instances of + * object::Object. + * + * Different sub-classes may be mixed, and you have to care about casting, + * there is no type-safety. + */ +namespace untyped { + +/** + * \brief ... + */ +class Collection0: public object::Object +{ + friend class Iterator; + +protected: + /** + * \brief The base class for all iterators, as created by + * container::untyped::Collection::createIterator. + */ + class AbstractIterator: public object::Object + { + private: + int refcount; + + public: + AbstractIterator() { refcount = 1; } + + void ref () { refcount++; } + void unref () { refcount--; if (refcount == 0) delete this; } + + virtual bool hasNext () = 0; + virtual Object *getNext () = 0; + }; + +protected: + virtual AbstractIterator* createIterator() = 0; +}; + +/** + * \brief This is a small wrapper for AbstractIterator, which may be used + * directly, not as a pointer, to makes memory management simpler. + */ +class Iterator +{ + friend class Collection; + +private: + Collection0::AbstractIterator *impl; + + // Should not instantiated directly. + inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; } + +public: + Iterator(); + Iterator(const Iterator &it2); + Iterator(Iterator &it2); + ~Iterator(); + Iterator &operator=(const Iterator &it2); + Iterator &operator=(Iterator &it2); + + inline bool hasNext() { return impl->hasNext(); } + inline object::Object *getNext() { return impl->getNext(); } +}; + +/** + * \brief Abstract base class for all container objects in container::untyped. + */ +class Collection: public Collection0 +{ +public: + void intoStringBuffer(misc::StringBuffer *sb); + inline Iterator iterator() { Iterator it(createIterator()); return it; } + virtual int size() = 0; +}; + + +/** + * \brief Container, which is implemented by an array, which is + * dynamically resized. + */ +class Vector: public Collection +{ + friend class VectorIterator; + +private: + object::Object **array; + int numAlloc, numElements; + bool ownerOfObjects; + + class VectorIterator: public AbstractIterator + { + private: + Vector *vector; + int index; + + public: + VectorIterator(Vector *vector) { this->vector = vector; index = -1; } + bool hasNext(); + Object *getNext(); + }; + +protected: + AbstractIterator* createIterator(); + +public: + Vector(int initSize, bool ownerOfObjects); + ~Vector(); + + int size(); + + void put(object::Object *newElement, int newPos = -1); + void insert(object::Object *newElement, int pos); + + /** + * \brief Insert into an already sorted vector. + * + * Notice that insertion is not very efficient, unless the position + * is rather at the end. + */ + inline int insertSorted(object::Object *newElement, + object::Comparator *comparator = + &object::standardComparator) + { int pos = bsearch (newElement, false, comparator); + insert (newElement, pos); return pos; } + + void remove(int pos); + inline object::Object *get(int pos) const + { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; } + void clear(); + void sort(object::Comparator *comparator = &object::standardComparator); + int bsearch(Object *key, bool mustExist, int start, int end, + object::Comparator *comparator = &object::standardComparator); + inline int bsearch(Object *key, bool mustExist, + object::Comparator *comparator = + &object::standardComparator) + { return bsearch (key, mustExist, 0, size () - 1, comparator); } +}; + + +/** + * \brief A single-linked list. + */ +class List: public Collection +{ + friend class ListIterator; + +private: + struct Node + { + public: + object::Object *object; + Node *next; + }; + + class ListIterator: public AbstractIterator + { + private: + List::Node *current; + public: + ListIterator(List::Node *node) { current = node; } + bool hasNext(); + Object *getNext(); + }; + + Node *first, *last; + bool ownerOfObjects; + int numElements; + + bool remove0(object::Object *element, bool compare, bool doNotDeleteAtAll); + +protected: + AbstractIterator* createIterator(); + +public: + List(bool ownerOfObjects); + ~List(); + + bool equals(Object *other); + int hashValue(); + + int size (); + + void clear(); + void append(object::Object *element); + bool insertBefore(object::Object *beforeThis, object::Object *neew); + inline bool removeRef(object::Object *element) + { return remove0(element, false, false); } + inline bool remove(object::Object *element) + { return remove0(element, true, false); } + inline bool detachRef(object::Object *element) + { return remove0(element, false, true); } + inline int size() const { return numElements; } + inline bool isEmpty() const { return numElements == 0; } + inline object::Object *getFirst() const { return first->object; } + inline object::Object *getLast() const { return last->object; } +}; + + +/** + * \brief A hash set. + */ +class HashSet: public Collection +{ + friend class HashSetIterator; + +protected: + struct Node + { + object::Object *object; + Node *next; + }; + + Node **table; + int tableSize, numElements; + bool ownerOfObjects; + + inline int calcHashValue(object::Object *object) const + { + return abs(object->hashValue()) % tableSize; + } + + virtual Node *createNode(); + virtual void clearNode(Node *node); + + Node *findNode(object::Object *object) const; + Node *insertNode(object::Object *object); + + AbstractIterator* createIterator(); + +private: + class HashSetIterator: public Collection0::AbstractIterator + { + private: + HashSet *set; + HashSet::Node *node; + int pos; + + void gotoNext(); + + public: + HashSetIterator(HashSet *set); + bool hasNext(); + Object *getNext(); + }; + +public: + HashSet(bool ownerOfObjects, int tableSize = 251); + ~HashSet(); + + int size (); + + void put (object::Object *object); + bool contains (object::Object *key) const; + bool remove (object::Object *key); + //Object *getReference (object::Object *object); +}; + +/** + * \brief A hash table. + */ +class HashTable: public HashSet +{ +private: + bool ownerOfValues; + + struct KeyValuePair: public Node + { + object::Object *value; + }; + +protected: + Node *createNode(); + void clearNode(Node *node); + +public: + HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251); + ~HashTable(); + + void intoStringBuffer(misc::StringBuffer *sb); + + void put (object::Object *key, object::Object *value); + object::Object *get (object::Object *key) const; +}; + +/** + * \brief A stack (LIFO). Can be used as Queue (FIFO) when pushUnder() + * is used instead of push(). + * + * Note that the iterator returns all elements in the reversed order they have + * been put on the stack. + */ +class Stack: public Collection +{ + friend class StackIterator; + +private: + class Node + { + public: + object::Object *object; + Node *prev; + }; + + class StackIterator: public AbstractIterator + { + private: + Stack::Node *current; + public: + StackIterator(Stack::Node *node) { current = node; } + bool hasNext(); + Object *getNext(); + }; + + Node *bottom, *top; + bool ownerOfObjects; + int numElements; + +protected: + AbstractIterator* createIterator(); + +public: + Stack (bool ownerOfObjects); + ~Stack(); + + int size (); + + void push (object::Object *object); + void pushUnder (object::Object *object); + inline object::Object *getTop () const { return top ? top->object : NULL; } + void pop (); + inline int size() const { return numElements; } +}; + +} // namespace untyped + +/** + * \brief This namespace provides thin wrappers, implemented as C++ templates, + * to gain type-safety. + * + * Notice, that nevertheless, all objects still have to be instances of + * object::Object. + */ +namespace typed { + +template <class T> class Collection; + +/** + * \brief Typed version of container::untyped::Iterator. + */ +template <class T> class Iterator +{ + friend class Collection<T>; + +private: + untyped::Iterator base; + +public: + inline Iterator() { } + inline Iterator(const Iterator<T> &it2) { this->base = it2.base; } + inline Iterator(Iterator<T> &it2) { this->base = it2.base; } + ~Iterator() { } + inline Iterator &operator=(const Iterator<T> &it2) + { this->base = it2.base; return *this; } + inline Iterator &operator=(Iterator<T> &it2) + { this->base = it2.base; return *this; } + + inline bool hasNext() { return this->base.hasNext(); } + inline T *getNext() { return (T*)this->base.getNext(); } +}; + +/** + * \brief Abstract base class template for all container objects in + * container::typed. + * + * Actually, a wrapper for container::untyped::Collection. + */ +template <class T> class Collection: public object::Object +{ +protected: + untyped::Collection *base; + +public: + Collection () { this->base = NULL; } + ~Collection () { if (this->base) delete this->base; } + + bool equals(Object *other) + { return this->base->equals (((Collection<T>*)other)->base); } + + int hashValue() { return this->base->hashValue (); } + + void intoStringBuffer(misc::StringBuffer *sb) + { this->base->intoStringBuffer(sb); } + inline Iterator<T> iterator() { + Iterator<T> it; it.base = this->base->iterator(); return it; } + inline int size() { return this->base->size (); } +}; + + +/** + * \brief Typed version of container::untyped::Vector. + */ +template <class T> class Vector: public Collection <T> +{ +public: + inline Vector(int initSize, bool ownerOfObjects) { + this->base = new untyped::Vector(initSize, ownerOfObjects); } + + inline void put(T *newElement, int newPos = -1) + { ((untyped::Vector*)this->base)->put(newElement, newPos); } + inline void insert(T *newElement, int pos) + { ((untyped::Vector*)this->base)->insert(newElement, pos); } + inline int insertSorted(T *newElement, + object::Comparator *comparator = + &object::standardComparator) + { return ((untyped::Vector*)this->base)->insertSorted(newElement, + comparator); } + inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); } + inline T *get(int pos) const + { return (T*)((untyped::Vector*)this->base)->get(pos); } + inline void clear() { ((untyped::Vector*)this->base)->clear(); } + inline void sort(object::Comparator *comparator = + &object::standardComparator) + { ((untyped::Vector*)this->base)->sort(comparator); } + inline int bsearch(T *key, bool mustExist, int start, int end, + object::Comparator *comparator = + &object::standardComparator) + { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end, + comparator); } + inline int bsearch(T *key, bool mustExist, + object::Comparator *comparator = + &object::standardComparator) + { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, + comparator); } +}; + + +/** + * \brief Typed version of container::untyped::List. + */ +template <class T> class List: public Collection <T> +{ +public: + inline List(bool ownerOfObjects) + { this->base = new untyped::List(ownerOfObjects); } + + inline void clear() { ((untyped::List*)this->base)->clear(); } + inline void append(T *element) + { ((untyped::List*)this->base)->append(element); } + inline bool insertBefore(object::Object *beforeThis, object::Object *neew) + { return ((untyped::List*)this->base)->insertBefore(beforeThis, neew); } + inline bool removeRef(T *element) { + return ((untyped::List*)this->base)->removeRef(element); } + inline bool remove(T *element) { + return ((untyped::List*)this->base)->remove(element); } + inline bool detachRef(T *element) { + return ((untyped::List*)this->base)->detachRef(element); } + + inline bool isEmpty() const + { return ((untyped::List*)this->base)->isEmpty(); } + inline T *getFirst() const + { return (T*)((untyped::List*)this->base)->getFirst(); } + inline T *getLast() const + { return (T*)((untyped::List*)this->base)->getLast(); } +}; + +/** + * \brief Typed version of container::untyped::HashSet. + */ +template <class T> class HashSet: public Collection <T> +{ +protected: + inline HashSet() { } + +public: + inline HashSet(bool owner, int tableSize = 251) + { this->base = new untyped::HashSet(owner, tableSize); } + + inline void put(T *object) + { return ((untyped::HashSet*)this->base)->put(object); } + inline bool contains(T *object) const + { return ((untyped::HashSet*)this->base)->contains(object); } + inline bool remove(T *object) + { return ((untyped::HashSet*)this->base)->remove(object); } + //inline T *getReference(T *object) + //{ return (T*)((untyped::HashSet*)this->base)->getReference(object); } +}; + +/** + * \brief Typed version of container::untyped::HashTable. + */ +template <class K, class V> class HashTable: public HashSet <K> +{ +public: + inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251) + { this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues, + tableSize); } + + inline void put(K *key, V *value) + { return ((untyped::HashTable*)this->base)->put(key, value); } + inline V *get(K *key) const + { return (V*)((untyped::HashTable*)this->base)->get(key); } +}; + +/** + * \brief Typed version of container::untyped::Stack. + */ +template <class T> class Stack: public Collection <T> +{ +public: + inline Stack (bool ownerOfObjects) + { this->base = new untyped::Stack (ownerOfObjects); } + + inline void push (T *object) { + ((untyped::Stack*)this->base)->push (object); } + inline void pushUnder (T *object) + { ((untyped::Stack*)this->base)->pushUnder (object); } + inline T *getTop () const + { return (T*)((untyped::Stack*)this->base)->getTop (); } + inline void pop () { ((untyped::Stack*)this->base)->pop (); } +}; + +} // namespace untyped + +} // namespace container + +} // namespace lout + +#endif // __LOUT_CONTAINER_HH_ diff --git a/lout/debug.hh b/lout/debug.hh new file mode 100644 index 0000000..636d4a4 --- /dev/null +++ b/lout/debug.hh @@ -0,0 +1,381 @@ +#ifndef __LOUT_DEBUG_H__ +#define __LOUT_DEBUG_H__ + +/* + * Simple debug messages. Add: + * + * #define DEBUG_LEVEL <n> + * #include "debug.h" + * + * to the file you are working on, or let DEBUG_LEVEL undefined to + * disable all messages. A higher level denotes a greater importance + * of the message. + */ + +#include <stdio.h> + +#define D_STMT_START do +#define D_STMT_END while (0) + +#define D_STMT_NOP D_STMT_START { } D_STMT_END + +# ifdef DEBUG_LEVEL +# define DEBUG_MSG(level, ...) \ + D_STMT_START { \ + if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \ + printf(__VA_ARGS__); \ + } D_STMT_END +# else +# define DEBUG_MSG(level, ...) +# endif /* DEBUG_LEVEL */ + + + +/* + * This is an old version of "debug_rtfl.hh", which has been copied + * from dillo (see README). + */ + +#ifdef DBG_RTFL + +#include <unistd.h> +#include <stdio.h> + +#define DBG_IF_RTFL if(1) + +// "\n" at the beginning just in case that the previous line is not finished +// yet. +#define RTFL_PREFIX_FMT "\n[rtfl]%s:%d:%d:" +#define RTFL_PREFIX_ARGS CUR_WORKING_DIR "/" __FILE__, __LINE__, getpid() + +#define DBG_OBJ_MSG(aspect, prio, msg) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, msg); \ + fflush (stdout); \ + } D_STMT_END + +// Variant which does not use "this", but an explicitly passed +// object. Should be applied to other macros. (Also, for use in C.) +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, msg); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, __VA_ARGS__); \ + fflush (stdout); \ + } D_STMT_END + +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, __VA_ARGS__); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_MSG_START() \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ + RTFL_PREFIX_ARGS, this); \ + fflush (stdout); \ + } D_STMT_END + +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSG_START_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ + RTFL_PREFIX_ARGS, obj); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_MSG_END() \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \ + RTFL_PREFIX_ARGS, this); \ + fflush (stdout); \ + } D_STMT_END + +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSG_END_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \ + RTFL_PREFIX_ARGS, obj); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ENTER0(aspect, prio, funname) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, funname); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, funname); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ + D_STMT_START { \ + fflush (stdout); \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, funname, __VA_ARGS__); \ + } D_STMT_END + +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ + D_STMT_START { \ + fflush (stdout); \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, funname, __VA_ARGS__); \ + } D_STMT_END + +#define DBG_OBJ_LEAVE() \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ + RTFL_PREFIX_ARGS, this); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_LEAVE_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ + RTFL_PREFIX_ARGS, obj); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_CREATE(klass) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-create:%p:%s\n", \ + RTFL_PREFIX_ARGS, this, klass); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_DELETE() \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-delete:%p\n", \ + RTFL_PREFIX_ARGS, this); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_BASECLASS(klass) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-ident:%p:%p\n", \ + RTFL_PREFIX_ARGS, this, (klass*)this); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ASSOC(parent, child) \ + D_STMT_START { \ + if (child) { \ + printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ + RTFL_PREFIX_ARGS, parent, child); \ + fflush (stdout); \ + } \ + } D_STMT_END + +#define DBG_OBJ_ASSOC_PARENT(parent) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ + RTFL_PREFIX_ARGS, parent, this); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ASSOC_CHILD(child) \ + D_STMT_START { \ + if (child) { \ + printf (RTFL_PREFIX_FMT "obj-assoc:%p:%p\n", \ + RTFL_PREFIX_ARGS, this, child); \ + fflush (stdout); \ + } \ + } D_STMT_END + +#define DBG_OBJ_SET_NUM(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \ + RTFL_PREFIX_ARGS, this, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_NUM_O(obj, var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \ + RTFL_PREFIX_ARGS, obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_SYM(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_STR(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_PTR(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ + RTFL_PREFIX_ARGS, this, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_PTR_O(obj, var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ + RTFL_PREFIX_ARGS, obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_BOOL(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, obj, var, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_NUM(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%d\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_SYM(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_STR(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_PTR(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%p\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%d\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%p\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_COLOR(klass, color) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-color:%s:%s\n", \ + RTFL_PREFIX_ARGS, color, klass); \ + fflush (stdout); \ + } D_STMT_END + +#else /* DBG_RTFL */ + +#define DBG_IF_RTFL if(0) + +#define DBG_OBJ_MSG(aspect, prio, msg) D_STMT_NOP +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) D_STMT_NOP +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) D_STMT_NOP +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) D_STMT_NOP +#define DBG_OBJ_MSG_START() D_STMT_NOP +#define DBG_OBJ_MSG_START_O(obj) D_STMT_NOP +#define DBG_OBJ_MSG_END() D_STMT_NOP +#define DBG_OBJ_MSG_END_O(obj) D_STMT_NOP +#define DBG_OBJ_ENTER0(aspect, prio, funname) D_STMT_NOP +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) D_STMT_NOP +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) D_STMT_NOP +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) D_STMT_NOP +#define DBG_OBJ_LEAVE() D_STMT_NOP +#define DBG_OBJ_LEAVE_O(obj) D_STMT_NOP +#define DBG_OBJ_CREATE(klass) D_STMT_NOP +#define DBG_OBJ_DELETE() D_STMT_NOP +#define DBG_OBJ_BASECLASS(klass) D_STMT_NOP +#define DBG_OBJ_ASSOC_PARENT(parent) D_STMT_NOP +#define DBG_OBJ_ASSOC_CHILD(child) D_STMT_NOP +#define DBG_OBJ_ASSOC(parent, child) D_STMT_NOP +#define DBG_OBJ_SET_NUM(var, val) D_STMT_NOP +#define DBG_OBJ_SET_NUM_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP +#define DBG_OBJ_SET_STR(var, val) D_STMT_NOP +#define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP +#define DBG_OBJ_SET_PTR_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_SET_BOOL(var, val) D_STMT_NOP +#define DBG_OBJ_SET_BOOL_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_STR(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_PTR(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_BOOL(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_COLOR(klass, color) D_STMT_NOP + +#endif /* DBG_RTFL */ + +#endif /* __LOUT_DEBUG_H__ */ + + diff --git a/lout/identity.cc b/lout/identity.cc new file mode 100644 index 0000000..a1664aa --- /dev/null +++ b/lout/identity.cc @@ -0,0 +1,141 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "identity.hh" + +#include <stdio.h> + +namespace lout { +namespace identity { + +// ------------------------ +// IdentifiableObject +// ------------------------ + +using namespace object; +using namespace container::typed; + +IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id, + const char *className) +{ + this->parent = parent; + this->id = id; + this->className = className; +} + +void IdentifiableObject::Class::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("<class "); + sb->append (className); + sb->append (" ("); + sb->appendInt (id); + sb->append (")"); + + if (parent) { + sb->append (", parent: "); + parent->intoStringBuffer (sb); + } + + sb->append (">"); +} + +HashTable <ConstString, IdentifiableObject::Class> + *IdentifiableObject::classesByName = + new HashTable<ConstString, IdentifiableObject::Class> (true, true); +Vector <IdentifiableObject::Class> *IdentifiableObject::classesById = + new Vector <IdentifiableObject::Class> (16, false); +IdentifiableObject::Class *IdentifiableObject::currentlyConstructedClass; + +IdentifiableObject::IdentifiableObject () +{ + currentlyConstructedClass = NULL; +} + +void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<instance "); + sb->appendPointer(this); + sb->append(" of "); + sb->append(getClassName()); + sb->append(">"); +} + +/** + * \brief This method must be called in the constructor for the sub class. + * See class comment for details. + */ +void IdentifiableObject::registerName (const char *className, int *classId) +{ + ConstString str (className); + Class *klass = classesByName->get (&str); + if (klass == NULL) { + klass = new Class (currentlyConstructedClass, classesById->size (), + className); + ConstString *key = new ConstString (className); + classesByName->put (key, klass); + classesById->put (klass); + *classId = klass->id; + } + + this->classId = klass->id; + *classId = klass->id; + currentlyConstructedClass = klass; +} + +/** + * \brief Returns, whether this class is an instance of the class, given by + * \em otherClassId, or of a sub class of this class. + */ +bool IdentifiableObject::instanceOf (int otherClassId) +{ + if (otherClassId == -1) + // Other class has not been registered yet, while it should have been, + // if this class is an instance of it or of a sub-class. + return false; + + Class *otherClass = classesById->get (otherClassId); + + if (otherClass == NULL) { + fprintf (stderr, + "WARNING: Something got wrong here, it seems that a " + "CLASS_ID was not initialized properly.\n"); + return false; + } + + for (Class *klass = classesById->get (classId); klass != NULL; + klass = klass->parent) { + if (klass == otherClass) + return true; + } + + return false; +} + +} // namespace identity +} // namespace lout diff --git a/lout/identity.hh b/lout/identity.hh new file mode 100644 index 0000000..df42b20 --- /dev/null +++ b/lout/identity.hh @@ -0,0 +1,149 @@ +#ifndef __LOUT_OBJECTX_HH__ +#define __LOUT_OBJECTX_HH__ + +#include "object.hh" +#include "container.hh" +#include "signal.hh" + +namespace lout { + +/** + * \brief Some stuff to identify classes of objects at run-time. + */ +namespace identity { + +/** + * \brief Instances of classes, which are sub classes of this class, may + * be identified at run-time. + * + * <h3>Testing the class</h3> + * + * Since e.g. dw::Textblock is a sub class of IdentifiableObject, and + * implemented in the correct way (as described below), for any given + * IdentifiableObject the following test can be done: + * + * \code + * identity::IdentifiableObject *o; + * // ... + * bool isATextblock = o->instanceOf(dw::Textblock::CLASS_ID); + * \endcode + * + * \em isATextblock is true, when \em o is an instance of dw::Textblock, + * or of a sub class of dw::Textblock. Otherwise, \em isATextblock is false. + * + * It is also possible to get the class identifier of an + * identity::IdentifiableObject, e.g. + * + * \code + * bool isOnlyATextblock = o->getClassId() == dw::Textblock::CLASS_ID; + * \endcode + * + * would result in true, if o is an instance of dw::Textblock, but not an + * instance of a sub class of dw::Textblock. + * + * <h3>Defining Sub Classes</h3> + * + * Each direct or indirect sub class of IdentifiableObject must + * + * <ul> + * <li> add a static int CLASS_ID with -1 as initial value, and + * <li> call registerName (\em name, &CLASS_ID) in the constructor, where + * \em name should be unique, e.g. the fully qualified class name. + * </ul> + * + * After this, <i>class</i>::CLASS_ID refers to a number, which denotes the + * class. (If this is still -1, since the class has not yet been instantiated, + * any test will fail, which is correct.) + * + * <h3>Notes on implementation</h3> + * + * If there are some classes like this: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * IdentifiableObject [color="#a0a0a0"]; + * A; + * B [color="#a0a0a0"]; + * C; + * IdentifiableObject -> A; + * IdentifiableObject -> B; + * B -> C; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * and first, an instance of A, and then an instance of C is created, there + * will be the following calls of functions and constructors: + * + * <ol> + * <li> %IdentifiableObject (); + * <li> %registerName ("A", &A::CLASS_ID); + * <li> %IdentifiableObject (); + * <li> %registerName ("B", &B::CLASS_ID); + * <li> %registerName ("C", &C::CLASS_ID); + * </ol> + * + * From this, the class hierarchy above can easily constructed, and stored + * in identity::IdentifiableObject::classesByName and + * in identity::IdentifiableObject::classesById. See the code for details. + * + * N.b. Multiple inheritance is not supported, the construction of the + * tree would become confused. + */ +class IdentifiableObject: public object::Object +{ +private: + class Class: public object::Object + { + public: + Class *parent; + int id; + const char *className; + + Class (Class *parent, int id, const char *className); + + void intoStringBuffer(misc::StringBuffer *sb); + }; + + static container::typed::HashTable <object::ConstString, + Class> *classesByName; + static container::typed::Vector <Class> *classesById; + static Class *currentlyConstructedClass; + + int classId; + +protected: + void registerName (const char *className, int *classId); + +public: + IdentifiableObject (); + + void intoStringBuffer(misc::StringBuffer *sb); + + /** + * \brief Returns the class identifier. + * + * This is rarely used, rather, tests with + * identity::IdentifiableObject::instanceOf are done. + */ + int getClassId () { return classId; } + + /** + * \brief Return the name, under which the class of this object was + * registered. + */ + const char *getClassName() { return classesById->get(classId)->className; } + + bool instanceOf (int otherClassId); +}; + +} // namespace identity + +} // namespace lout + +#endif // __LOUT_OBJECTX_HH__ diff --git a/lout/misc.cc b/lout/misc.cc new file mode 100644 index 0000000..5064484 --- /dev/null +++ b/lout/misc.cc @@ -0,0 +1,197 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "misc.hh" + +#include <ctype.h> +#include <config.h> + +#define PRGNAME PACKAGE "/" VERSION + +namespace lout { + +namespace misc { + +const char *prgName = PRGNAME; + +void init (int argc, char *argv[]) +{ + prgName = strdup (argv[0]); +} + + +// ------------------ +// StringBuffer +// ------------------ + + +StringBuffer::StringBuffer() +{ + firstNode = lastNode = NULL; + numChars = 0; + str = NULL; + strValid = false; +} + +StringBuffer::~StringBuffer() +{ + clear (); + if (str) + delete[] str; +} + +/** + * \brief Append a NUL-terminated string to the buffer, without copying. + * + * No copy is made, so this method should only be used in cases, where + * the string would otherwise be freed again. (This method may then + * save some CPU cycles.) + */ +void StringBuffer::appendNoCopy(char *str) +{ + Node *node = new Node(); + node->data = str; + node->next = NULL; + + if (firstNode == NULL) { + firstNode = node; + lastNode = node; + } else { + lastNode->next = node; + lastNode = node; + } + + numChars += strlen(str); + strValid = false; +} + +/** + * \brief Return a NUL-terminated strings containing all appended strings. + * + * The caller does not have to free the string, this is done in + * misc::StringBuffer::~StringBuffer. + */ +const char *StringBuffer::getChars() +{ + if (strValid) + return str; + + if (str) + delete[] str; + str = new char[numChars + 1]; + char *p = str; + + for (Node *node = firstNode; node; node = node->next) { + int l = strlen(node->data); + memcpy(p, node->data, l * sizeof(char)); + p += l; + } + + *p = 0; + strValid = true; + return str; +} + +/** + * \brief Remove all strings appended to the string buffer. + */ +void StringBuffer::clear () +{ + Node *node, *nextNode; + for (node = firstNode; node; node = nextNode) { + nextNode = node->next; + free(node->data); + delete node; + } + firstNode = lastNode = NULL; + numChars = 0; + strValid = false; +} + + +// ------------ +// BitSet +// ------------ + +BitSet::BitSet(int initBits) +{ + numBytes = bytesForBits(initBits); + bits = (unsigned char*)malloc(numBytes * sizeof(unsigned char)); + clear(); +} + +BitSet::~BitSet() +{ + free(bits); +} + +void BitSet::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("["); + for (int i = 0; i < numBytes; i++) + sb->append(get(i) ? "1" : "0"); + sb->append("]"); +} + +bool BitSet::get(int i) const +{ + if (8 * i >= numBytes) + return false; + else + return bits[i / 8] & (1 << (i % 8)); +} + +void BitSet::set(int i, bool val) +{ + if (8 * i >= numBytes) { + int newNumBytes = numBytes; + while (8 * i >= newNumBytes) + newNumBytes *= 2; + bits = + (unsigned char*)realloc(bits, newNumBytes * sizeof(unsigned char)); + memset(bits + numBytes, 0, newNumBytes - numBytes); + numBytes = newNumBytes; + } + + if (val) + bits[i / 8] |= (1 << (i % 8)); + else + bits[i / 8] &= ~(1 << (i % 8)); +} + +void BitSet::clear() +{ + memset(bits, 0, numBytes); +} + +} // namespace misc + +} // namespace lout diff --git a/lout/misc.hh b/lout/misc.hh new file mode 100644 index 0000000..7edf83c --- /dev/null +++ b/lout/misc.hh @@ -0,0 +1,652 @@ +#ifndef __LOUT_MISC_HH__ +#define __LOUT_MISC_HH__ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +namespace lout { + +/** + * \brief Miscellaneous stuff, which does not fit anywhere else. + * + * Actually, the other parts, beginning with ::object, depend on this. + */ +namespace misc { + +template <class T> inline T min (T a, T b) { return a < b ? a : b; } +template <class T> inline T max (T a, T b) { return a > b ? a : b; } + +template <class T> inline T min (T a, T b, T c) +{ + return (min (a, min (b, c))); +} +template <class T> inline T max (T a, T b, T c) +{ + return (max (a, max (b, c))); +} + +extern const char *prgName; + +void init (int argc, char *argv[]); + +inline void assertNotReached () +{ + fprintf (stderr, "*** [%s] This should not happen! ***\n", prgName); + abort (); +} + +inline int roundInt(double d) +{ + return (int) ((d > 0) ? (d + 0.5) : (d - 0.5)); +} + +inline int AsciiTolower(char c) +{ + return ((c >= 'A' && c <= 'Z') ? c + 0x20 : c); +} + +inline int AsciiToupper(char c) +{ + return ((c >= 'a' && c <= 'z') ? c - 0x20 : c); +} + +inline int AsciiStrcasecmp(const char *s1, const char *s2) +{ + int ret = 0; + + while ((*s1 || *s2) && !(ret = AsciiTolower(*s1) - AsciiTolower(*s2))) { + s1++; + s2++; + } + return ret; +} + +/** + * \brief Simple (simpler than container::untyped::Vector and + * container::typed::Vector) template based vector. + */ +template <class T> class SimpleVector +{ +private: + T *array; + int num, numAlloc; + + inline void resize () + { + /* This algorithm was tuned for memory&speed with this huge page: + * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz + */ + if (array == NULL) { + this->numAlloc = 1; + this->array = (T*) malloc (sizeof (T)); + } + if (this->numAlloc < this->num) { + this->numAlloc = (this->num < 100) ? + this->num : this->num + this->num/10; + this->array = + (T*) realloc(this->array, (this->numAlloc * sizeof (T))); + } + } + +public: + inline SimpleVector (int initAlloc = 1) + { + this->num = 0; + this->numAlloc = initAlloc; + this->array = NULL; + } + + inline SimpleVector (const SimpleVector &o) { + this->array = NULL; + this->num = o.num; + this->numAlloc = o.numAlloc; + resize (); + memcpy (this->array, o.array, sizeof (T) * num); + } + + inline ~SimpleVector () + { + if (this->array) + free (this->array); + } + + /** + * \brief Return the number of elements put into this vector. + */ + inline int size() const { return this->num; } + + inline T* getArray() const { return array; } + + inline T* detachArray() { + T* arr = array; + array = NULL; + numAlloc = 0; + num = 0; + return arr; + } + + /** + * \brief Increase the vector size by one. + * + * May be necessary before calling misc::SimpleVector::set. + */ + inline void increase() { setSize(this->num + 1); } + + /** + * \brief Set the size explicitly. + * + * May be necessary before calling misc::SimpleVector::set. + */ + inline void setSize(int newSize) { + assert (newSize >= 0); + this->num = newSize; + this->resize (); + } + + /** + * \brief Set the size explicitly and initialize new values. + * + * May be necessary before calling misc::SimpleVector::set. + */ + inline void setSize (int newSize, T t) { + int oldSize = this->num; + setSize (newSize); + for (int i = oldSize; i < newSize; i++) + set (i, t); + } + + /** + * \brief Return the reference of one element. + * + * \sa misc::SimpleVector::get + */ + inline T* getRef (int i) const { + assert (i >= 0 && this->num - i > 0); + return array + i; + } + + /** + * \brief Return the one element, explicitly. + * + * The element is copied, so for complex elements, you should rather used + * misc::SimpleVector::getRef. + */ + inline T get (int i) const { + assert (i >= 0 && this->num - i > 0); + return this->array[i]; + } + + /** + * \brief Return the reference of the first element (convenience method). + */ + inline T* getFirstRef () const { + assert (this->num > 0); + return this->array; + } + + /** + * \brief Return the first element, explicitly. + */ + inline T getFirst () const { + assert (this->num > 0); + return this->array[0]; + } + + /** + * \brief Return the reference of the last element (convenience method). + */ + inline T* getLastRef () const { + assert (this->num > 0); + return this->array + this->num - 1; + } + + /** + * \brief Return the last element, explicitly. + */ + inline T getLast () const { + assert (this->num > 0); + return this->array[this->num - 1]; + } + + /** + * \brief Store an object in the vector. + * + * Unlike in container::untyped::Vector and container::typed::Vector, + * you have to care about the size, so a call to + * misc::SimpleVector::increase or misc::SimpleVector::setSize may + * be necessary before. + */ + inline void set (int i, T t) { + assert (i >= 0 && this->num - i > 0); + this->array[i] = t; + } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + assert (this->num > 0); + this->array[this->num - 1] = t; + } +}; + +/** + * \brief Container similar to lout::misc::SimpleVector, but some cases + * of insertion optimized (used for hyphenation). + * + * For hyphenation, words are often split, so that some space must be + * inserted by the method NotSoSimpleVector::insert. Typically, some + * elements are inserted quite at the beginning (when the word at the + * end of the first or at the beginning of the second line is + * hyphenated), then, a bit further (end of second line/beginning of + * third line) and so on. In the first time, nearly all words must be + * moved; in the second time, a bit less, etc. After all, using a + * simple vector would result in O(n<sup>2</sup>) number of elements + * moved total. With this class, however, the number can be kept at + * O(n). + * + * The basic idea is to keep an extra array (actually two, of which + * the second one is used temporarily), which is inserted in a logical + * way. Since there is only one extra array at max, reading is rather + * simple and fast (see NotSoSimpleVector::getRef): check whether the + * position is before, within, or after the extra array. The first + * insertion is also rather simple, when the extra array has to be + * created. The following sketch illustrates the most complex case, + * when an extra array exists, and something is inserted after it (the + * case for which this class has been optimized): + * + * \image html not-so-simple-container.png + * + * Dotted lines are used to keep the boxes aligned. + * + * As you see, only a relatively small fraction of elements has to be + * moved. + * + * There are some other cases, which have to be documented. + */ +template <class T> class NotSoSimpleVector +{ +private: + T *arrayMain, *arrayExtra1, *arrayExtra2; + int numMain, numExtra, numAllocMain, numAllocExtra, startExtra; + + inline void resizeMain () + { + /* This algorithm was tuned for memory&speed with this huge page: + * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz + */ + if (arrayMain == NULL) { + this->numAllocMain = 1; + this->arrayMain = (T*) malloc (sizeof (T)); + } + if (this->numAllocMain < this->numMain) { + this->numAllocMain = (this->numMain < 100) ? + this->numMain : this->numMain + this->numMain/10; + this->arrayMain = + (T*) realloc(this->arrayMain, (this->numAllocMain * sizeof (T))); + } + } + + inline void resizeExtra () + { + /* This algorithm was tuned for memory&speed with this huge page: + * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz + */ + if (arrayExtra1 == NULL) { + this->numAllocExtra = 1; + this->arrayExtra1 = (T*) malloc (sizeof (T)); + this->arrayExtra2 = (T*) malloc (sizeof (T)); + } + if (this->numAllocExtra < this->numExtra) { + this->numAllocExtra = (this->numExtra < 100) ? + this->numExtra : this->numExtra + this->numExtra/10; + this->arrayExtra1 = + (T*) realloc(this->arrayExtra1, (this->numAllocExtra * sizeof (T))); + this->arrayExtra2 = + (T*) realloc(this->arrayExtra2, (this->numAllocExtra * sizeof (T))); + } + } + + void consolidate () + { + if (startExtra != -1) { + numMain += numExtra; + resizeMain (); + memmove (arrayMain + startExtra + numExtra, arrayMain + startExtra, + (numMain - (startExtra + numExtra)) * sizeof (T)); + memmove (arrayMain + startExtra, arrayExtra1, numExtra * sizeof (T)); + startExtra = -1; + numExtra = 0; + } + } + +public: + inline NotSoSimpleVector (int initAlloc) + { + this->numMain = this->numExtra = 0; + this->numAllocMain = initAlloc; + this->numAllocExtra = initAlloc; + this->arrayMain = this->arrayExtra1 = this->arrayExtra2 = NULL; + this->startExtra = -1; + } + + inline NotSoSimpleVector (const NotSoSimpleVector &o) + { + this->arrayMain = NULL; + this->numMain = o.numMain; + this->numAllocMain = o.numAllocMain; + resizeMain (); + memcpy (this->arrayMain, o.arrayMain, sizeof (T) * numMain); + + this->arrayExtra = NULL; + this->numExtra = o.numExtra; + this->numAllocExtra = o.numAllocExtra; + resizeExtra (); + memcpy (this->arrayExtra, o.arrayExtra, sizeof (T) * numExtra); + + this->startExtra = o.startExtra; + } + + inline ~NotSoSimpleVector () + { + if (this->arrayMain) + free (this->arrayMain); + if (this->arrayExtra1) + free (this->arrayExtra1); + if (this->arrayExtra2) + free (this->arrayExtra2); + } + + inline int size() const { return this->numMain + this->numExtra; } + + inline void increase() { setSize(size() + 1); } + + inline void setSize(int newSize) + { + assert (newSize >= 0); + this->numMain = newSize - numExtra; + this->resizeMain (); + } + + void insert (int index, int numInsert) + { + assert (numInsert >= 0); + + // The following lines are a simple (but inefficient) replacement. + //setSize (numMain + numInsert); + //memmove (arrayMain + index + numInsert, arrayMain + index, + // (numMain - index - numInsert) * sizeof (T)); + //return; + + if (this->startExtra == -1) { + // simple case + this->numExtra = numInsert; + this->startExtra = index; + resizeExtra (); + } else { + if (index < startExtra) { + consolidate (); + insert (index, numInsert); + } else if (index < startExtra + numExtra) { + int oldNumExtra = numExtra; + numExtra += numInsert; + resizeExtra (); + + int toMove = startExtra + oldNumExtra - index; + memmove (arrayExtra1 + numExtra - toMove, + arrayExtra1 + index - startExtra, + toMove * sizeof (T)); + } else { + int oldNumExtra = numExtra; + numExtra += numInsert; + resizeExtra (); + + // Note: index refers to the *logical* adress, not to the + // *physical* one. + int diff = index - this->startExtra - oldNumExtra; + T *arrayMainI = arrayMain + this->startExtra; + for (int i = diff + oldNumExtra - 1; i >= 0; i--) { + T *src = i < oldNumExtra ? + this->arrayExtra1 + i : arrayMainI + (i - oldNumExtra); + T *dest = i < diff ? + arrayMainI + i : arrayExtra2 + (i - diff); + *dest = *src; + } + + memcpy (arrayExtra1, arrayExtra2, sizeof (T) * oldNumExtra); + startExtra = index - oldNumExtra; + } + } + } + + /** + * \brief Return the reference of one element. + * + * \sa misc::SimpleVector::get + */ + inline T* getRef (int i) const + { + if (this->startExtra == -1) { + assert (i >= 0 && i < this->numMain); + return this->arrayMain + i; + } else { + if (i < this->startExtra) { + assert (i >= 0); + return this->arrayMain + i; + } else if (i >= this->startExtra + this->numExtra) { + // The original assertion + /// + // "assert (i < this->numMain + this->numExtra)" + // + // causes this warnung in dw::Textblock::breakAdded: + // + // "assuming signed overflow does not occur when assuming that + // (X - c) > X is always false [-Wstrict-overflow]" + // + // Subtracting numExtra from both sides solves this, + // interrestingly. + + assert (i - this->numExtra < this->numMain); + return this->arrayMain + i - this->numExtra; + } else + return this->arrayExtra1 + i - this->startExtra; + } + } + + /** + * \brief Return the one element, explicitly. + * + * The element is copied, so for complex elements, you should rather used + * misc::SimpleVector::getRef. + */ + inline T get (int i) const + { + return *(this->getRef(i)); + } + + /** + * \brief Return the reference of the first element (convenience method). + */ + inline T* getFirstRef () const { + assert (size () > 0); + return this->getRef(0); + } + + /** + * \brief Return the first element, explicitly. + */ + inline T getFirst () const { + return *(this->getFirstRef()); + } + + /** + * \brief Return the reference of the last element (convenience method). + */ + inline T* getLastRef () const { + assert (size () > 0); + return this->getRef(size () - 1); + } + + /** + * \brief Return the last element, explicitly. + */ + inline T getLast () const { + return *(this->getLastRef()); + } + + /** + * \brief Store an object in the vector. + * + * Unlike in container::untyped::Vector and container::typed::Vector, + * you have to care about the size, so a call to + * misc::SimpleVector::increase or misc::SimpleVector::setSize may + * be necessary before. + */ + inline void set (int i, T t) { + *(this->getRef(i)) = t; + } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + *(this->getLastRef()) = t; + } +}; + +/** + * \brief A class for fast concatenation of a large number of strings. + */ +class StringBuffer +{ +private: + struct Node + { + char *data; + Node *next; + }; + + Node *firstNode, *lastNode; + int numChars; + char *str; + bool strValid; + +public: + StringBuffer(); + ~StringBuffer(); + + /** + * \brief Append a NUL-terminated string to the buffer, with copying. + * + * A copy is kept in the buffer, so the caller does not have to care + * about memory management. + */ + inline void append(const char *str) { appendNoCopy(strdup(str)); } + inline void appendInt(int n) + { char buf[32]; sprintf (buf, "%d", n); append (buf); } + inline void appendPointer(void *p) + { char buf[32]; sprintf (buf, "%p", p); append (buf); } + inline void appendBool(bool b) { append (b ? "true" : "false"); } + void appendNoCopy(char *str); + const char *getChars(); + void clear (); +}; + + +/** + * \brief A bit set, which automatically reallocates when needed. + */ +class BitSet +{ +private: + unsigned char *bits; + int numBytes; + + inline int bytesForBits(int bits) { return bits == 0 ? 1 : (bits + 7) / 8; } + +public: + BitSet(int initBits); + ~BitSet(); + void intoStringBuffer(misc::StringBuffer *sb); + bool get(int i) const; + void set(int i, bool val); + void clear(); +}; + +/** + * \brief A simple allocator optimized to handle many small chunks of memory. + * The chunks can not be free'd individually. Instead the whole zone must be + * free'd with zoneFree(). + */ +class ZoneAllocator +{ +private: + size_t poolSize, poolLimit, freeIdx; + SimpleVector <char*> *pools; + SimpleVector <char*> *bulk; + +public: + ZoneAllocator (size_t poolSize) { + this->poolSize = poolSize; + this->poolLimit = poolSize / 4; + this->freeIdx = poolSize; + this->pools = new SimpleVector <char*> (1); + this->bulk = new SimpleVector <char*> (1); + }; + + ~ZoneAllocator () { + zoneFree (); + delete pools; + delete bulk; + } + + inline void * zoneAlloc (size_t t) { + void *ret; + + if (t > poolLimit) { + bulk->increase (); + bulk->set (bulk->size () - 1, (char*) malloc (t)); + return bulk->get (bulk->size () - 1); + } + + if (t > poolSize - freeIdx) { + pools->increase (); + pools->set (pools->size () - 1, (char*) malloc (poolSize)); + freeIdx = 0; + } + + ret = pools->get (pools->size () - 1) + freeIdx; + freeIdx += t; + return ret; + } + + inline void zoneFree () { + for (int i = 0; i < pools->size (); i++) + free (pools->get (i)); + pools->setSize (0); + for (int i = 0; i < bulk->size (); i++) + free (bulk->get (i)); + bulk->setSize (0); + freeIdx = poolSize; + } + + inline const char *strndup (const char *str, size_t t) { + char *new_str = (char *) zoneAlloc (t + 1); + memcpy (new_str, str, t); + new_str[t] = '\0'; + return new_str; + } + + inline const char *strdup (const char *str) { + return strndup (str, strlen (str)); + } +}; + +} // namespace misc + +} // namespace lout + +#endif // __LOUT_MISC_HH__ diff --git a/lout/msg.h b/lout/msg.h new file mode 100644 index 0000000..4993c10 --- /dev/null +++ b/lout/msg.h @@ -0,0 +1,39 @@ +#ifndef __MSG_H__ +#define __MSG_H__ + +#include <stdio.h> + +#define prefs_show_msg 1 + +#define D_STMT_START do +#define D_STMT_END while (0) + +/* + * You can disable any MSG* macro by adding the '_' prefix. + */ +#define _MSG(...) +#define _MSG_WARN(...) +#define _MSG_ERR(...) + + +#define MSG(...) \ + D_STMT_START { \ + if (prefs_show_msg){ \ + printf(__VA_ARGS__); \ + fflush (stdout); \ + } \ + } D_STMT_END + +#define MSG_WARN(...) \ + D_STMT_START { \ + if (prefs_show_msg) \ + printf("** WARNING **: " __VA_ARGS__); \ + } D_STMT_END + +#define MSG_ERR(...) \ + D_STMT_START { \ + if (prefs_show_msg) \ + printf("** ERROR **: " __VA_ARGS__); \ + } D_STMT_END + +#endif /* __MSG_H__ */ diff --git a/lout/object.cc b/lout/object.cc new file mode 100644 index 0000000..933e7aa --- /dev/null +++ b/lout/object.cc @@ -0,0 +1,395 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "object.hh" +#include <stdio.h> +#include <stdint.h> +#include <config.h> + +namespace lout { + +namespace object { + +// ------------ +// Object +// ------------ + +/** + * \brief The destructor is defined as virtual (but not abstract), so that + * destruction of Object's works properly. + */ +Object::~Object() +{ +} + +/** + * \brief Returns, whether two objects are equal. + * + * The caller should ensure, that this and the object have the same class; + * this makes casting of "other" safe. Typically, an implementation should + * check this == other first, the caller can assume a fast implementation. + */ +bool Object::equals(Object *other) +{ + misc::assertNotReached (); + return false; +} + +/** + * \brief Return a hash value for the object. + */ +int Object::hashValue() +{ + fprintf (stderr, "Object::hashValue() should be implemented.\n"); + return 0; +} + +/** + * \brief Return an exact copy of the object. + */ +Object *Object::clone() +{ + misc::assertNotReached (); + return NULL; +} + +/** + * \brief Use object::Object::intoStringBuffer to return a textual + * representation of the object. + * + * The caller does not have to free the memory, object::Object is responsible + * for this. + */ +const char *Object::toString() +{ + /** \todo garbage! */ + misc::StringBuffer sb; + intoStringBuffer(&sb); + char *s = strdup(sb.getChars()); + return s; +} + +/** + * \brief Store a textual representation of the object in a misc::StringBuffer. + * + * This is used by object::Object::toString. + */ +void Object::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<not further specified object "); + sb->appendPointer(this); + sb->append(">"); +} + +/** + * \brief Return the number of bytes, this object totally uses. + */ +size_t Object::sizeOf() +{ + fprintf (stderr, "Object::sizeOf() should be implemented.\n"); + return sizeof(Object*); +} + +// ---------------- +// Comparator +// ---------------- + +Comparator *Comparator::compareFunComparator = NULL; + +/** + * \brief This static method may be used as compare function for + * qsort(3) and bsearch(3), for an array of Object* (Object*[] or + * Object**). + * + * "compareFunComparator" should be set before. + * + * \todo Not reentrant. Consider switching to reentrant variants + * (qsort_r), and compare function with an additional argument. + */ +int Comparator::compareFun(const void *p1, const void *p2) +{ + return compareFunComparator->compare (*(Object**)p1, *(Object**)p2); +} + +// ------------------------ +// StandardComparator +// ------------------------ + +int StandardComparator::compare(Object *o1, Object *o2) +{ + if (o1 && o2) + return ((Comparable*)o1)->compareTo ((Comparable*)o2); + else if (o1) + return 1; + else if (o2) + return -1; + else + return 0; +} + +StandardComparator standardComparator; + +// ------------- +// Pointer +// ------------- + +bool Pointer::equals(Object *other) +{ + return value == ((Pointer*)other)->value; +} + +int Pointer::hashValue() +{ +/* For some unknown reason, this doesn't compile on some 64bit platforms: + * + * if (sizeof (int) == sizeof (void*)) + * return (int)value; + * else + * return ((int*)&value)[0] ^ ((int*)&value)[1]; + */ +#if SIZEOF_VOID_P == 4 + // Assuming that sizeof(void*) == sizeof(int); on 32 bit systems. + return (int)value; +#else + // Assuming that sizeof(void*) == 2 * sizeof(int); on 64 bit + // systems (int is still 32 bit). + // Combine both parts of the pointer value *itself*, not what it + // points to, by first referencing it (operator "&"), then + // dereferencing it again (operator "[]"). + return ((intptr_t)value >> 32) ^ ((intptr_t)value); +#endif +} + +void Pointer::intoStringBuffer(misc::StringBuffer *sb) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "%p", value); + sb->append(buf); +} + +// ------------- +// Integer +// ------------- + +bool Integer::equals(Object *other) +{ + return value == ((Integer*)other)->value; +} + +int Integer::hashValue() +{ + return (int)value; +} + +void Integer::intoStringBuffer(misc::StringBuffer *sb) +{ + char buf[64]; + sprintf(buf, "%d", value); + sb->append(buf); +} + +int Integer::compareTo(Comparable *other) +{ + return value - ((Integer*)other)->value; +} + +// ------------- +// Boolean +// ------------- + +bool Boolean::equals(Object *other) +{ + bool value2 = ((Boolean*)other)->value; + // TODO Does "==" work? + return (value && value2) || (!value && value2); +} + +int Boolean::hashValue() +{ + return value ? 1 : 0; +} + +void Boolean::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append(value ? "true" : "false"); +} + +int Boolean::compareTo(Comparable *other) +{ + return (value ? 1 : 0) - (((Boolean*)other)->value ? 1 : 0); +} + +// ----------------- +// ConstString +// ----------------- + +bool ConstString::equals(Object *other) +{ + ConstString *otherString = (ConstString*)other; + return + this == other || + (str == NULL && otherString->str == NULL) || + (str != NULL && otherString->str != NULL && + strcmp(str, otherString->str) == 0); +} + +int ConstString::hashValue() +{ + return hashValue(str); +} + + +int ConstString::compareTo(Comparable *other) +{ + String *otherString = (String*)other; + if (str && otherString->str) + return strcmp(str, otherString->str); + else if (str) + return 1; + else if (otherString->str) + return -1; + else + return 0; +} + + +int ConstString::hashValue(const char *str) +{ + if (str) { + int h = 0; + for (int i = 0; str[i]; i++) + h = (h * 256 + str[i]); + return h; + } else + return 0; +} + +void ConstString::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append(str); +} + +// ------------ +// String +// ------------ + +String::String (const char *str): ConstString (str ? strdup(str) : NULL) +{ +} + +String::~String () +{ + if (str) + free((char *)str); +} + +// ------------ +// Pair +// ------------ + +PairBase::PairBase(Object *first, Object *second) +{ + this->first = first; + this->second = second; +} + +PairBase::~PairBase() +{ + if (first) + delete first; + if (second) + delete second; +} + +bool PairBase::equals(Object *other) +{ + PairBase *otherPair = (PairBase*)other; + + return + // Identical? + this == other || ( + (// Both first parts are NULL, ... + (first == NULL && otherPair->first == NULL) || + // ... or both first parts are not NULL and equal + (first != NULL && otherPair->first != NULL + && first->equals (otherPair->first))) && + // Same with second part. + ((second == NULL && otherPair->second == NULL) || + (second != NULL && otherPair->second != NULL + && second->equals (otherPair->second)))); +} + +int PairBase::hashValue() +{ + int value = 0; + + if (first) + value ^= first->hashValue(); + if (second) + value ^= second->hashValue(); + + return value; +} + +void PairBase::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<pair: "); + + if (first) + first->intoStringBuffer(sb); + else + sb->append("(nil)"); + + sb->append(","); + + if (second) + second->intoStringBuffer(sb); + else + sb->append("(nil)"); + + sb->append(">"); +} + +size_t PairBase::sizeOf() +{ + size_t size = 0; + + if (first) + size += first->sizeOf(); + if (second) + size += second->sizeOf(); + + return size; +} + +} // namespace object + +} // namespace lout diff --git a/lout/object.hh b/lout/object.hh new file mode 100644 index 0000000..3ba7b59 --- /dev/null +++ b/lout/object.hh @@ -0,0 +1,238 @@ +#ifndef __LOUT_OBJECT_HH__ +#define __LOUT_OBJECT_HH__ + +#include <stdlib.h> +#include <string.h> + +#include "misc.hh" + +namespace lout { + +/** + * \brief Here, some common classes (or interfaces) are defined, to standardize + * the access to other classes. + */ +namespace object { + +/** + * \brief This is the base class for many other classes, which defines very + * common virtual methods. + * + * For convenience, none of them are abstract, but they + * must be defined, when they are needed, especially for containers. + */ +class Object +{ +public: + virtual ~Object(); + virtual bool equals(Object *other); + virtual int hashValue(); + virtual Object *clone(); + virtual void intoStringBuffer(misc::StringBuffer *sb); + const char *toString(); + virtual size_t sizeOf(); +}; + +/** + * \brief Instances of a sub class of may be compared (less, greater). + * + * Used for sorting etc. + */ +class Comparable: public Object +{ +public: + /** + * \brief Compare two objects, this and other. + * + * Return a value < 0, when this is less than other, a value > 0, + * when this is greater than other, or 0, when this and other are + * equal. + * + * If c1.equals(c2) (as defined in Object), c1.compareTo(c2) must + * be 0, but, unlike you may expect, the reversed is not + * necessarily true. This method returns 0, if, according to the + * rules for sorting, there is no difference, but there may still + * be differences (not relevant for sorting), which "equals" will + * care about. + */ + virtual int compareTo(Comparable *other) = 0; +}; + +/** + * \brief Used for other orders as the one defined by Comparable. + * + * Compared objects must not neccessary be instances of Comparable. + */ +class Comparator: public Object +{ +public: + /** + * \brief Compare two objects o1 and o2. + * + * Return a value < 0, when o1 is less than o2, a value > 0, when o1 + * is greater than o2, or 0, when o1 and o2 are equal. + * + * If o1.equals(o2) (as defined in Object), compare(o1, o2) must be + * 0, but, unlike you may expect, the reversed is not necessarily + * true. This method returns 0, if, according to the rules for + * sorting, there is no difference, but there may still be + * differences (not relevant for sorting), which "equals" will care + * about. + */ + virtual int compare(Object *o1, Object *o2) = 0; + + static Comparator *compareFunComparator; + static int compareFun(const void *p1, const void *p2); +}; + +class StandardComparator: public Comparator +{ +public: + int compare(Object *o1, Object *o2); +}; + +extern StandardComparator standardComparator; + +/** + * \brief An object::Object wrapper for void pointers. + */ +class Pointer: public Object +{ +private: + void *value; + +public: + Pointer(void *value) { this->value = value; } + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + inline void *getValue() { return value; } +}; + +/** + * \brief A typed version of object::Pointer. + */ +template <class T> class TypedPointer: public Pointer +{ +public: + inline TypedPointer(T *value) : Pointer ((void*)value) { } + inline T *getTypedValue() { return (T*)getValue(); } +}; + + +/** + * \brief An object::Object wrapper for int's. + */ +class Integer: public Comparable +{ + int value; + +public: + Integer(int value) { this->value = value; } + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + int compareTo(Comparable *other); + inline int getValue() { return value; } +}; + + +/** + * \brief An object::Object wrapper for bool's. + */ +class Boolean: public Comparable +{ + bool value; + +public: + Boolean(bool value) { this->value = value; } + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + int compareTo(Comparable *other); + inline bool getValue() { return value; } +}; + + +/** + * \brief An object::Object wrapper for constant strings (char*). + * + * As opposed to object::String, the char array is not copied. + */ +class ConstString: public Comparable +{ +protected: + const char *str; + +public: + ConstString(const char *str) { this->str = str; } + bool equals(Object *other); + int hashValue(); + int compareTo(Comparable *other); + void intoStringBuffer(misc::StringBuffer *sb); + + inline const char *chars() { return str; } + + static int hashValue(const char *str); +}; + + +/** + * \brief An object::Object wrapper for strings (char*). + * + * As opposed to object::ConstantString, the char array is copied. + */ +class String: public ConstString +{ +public: + String(const char *str); + ~String(); +}; + +/** + * \todo Comment + */ +class PairBase: public Object +{ +protected: + Object *first, *second; + +public: + PairBase(Object *first, Object *second); + ~PairBase(); + + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + size_t sizeOf(); +}; + +/** + * \todo Comment + */ +class Pair: public PairBase +{ +public: + Pair(Object *first, Object *second): PairBase (first, second) { } + + inline Object *getFirst () { return first; } + inline Object *getSecond () { return second; } +}; + +/** + * \todo Comment + */ +template <class F, class S> class TypedPair: public PairBase +{ +public: + TypedPair(F *first, S *second): PairBase (first, second) { } + + inline F *getFirst () { return first; } + inline S *getSecond () { return second; } +}; + +} // namespace object + +} // namespace lout + +#endif // __LOUT_OBJECT_HH__ diff --git a/lout/signal.cc b/lout/signal.cc new file mode 100644 index 0000000..be92d2c --- /dev/null +++ b/lout/signal.cc @@ -0,0 +1,180 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "signal.hh" + +namespace lout { +namespace signal { + +using namespace container::typed; + +// ------------ +// Emitter +// ------------ + +Emitter::Emitter () +{ + receivers = new List <Receiver> (false); +} + +Emitter::~Emitter () +{ + for (Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext (); + receiver->unconnectFrom (this); + } + delete receivers; +} + +void Emitter::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("<emitter: "); + receivers->intoStringBuffer (sb); + sb->append (">"); +} + +void Emitter::unconnect (Receiver *receiver) +{ + receivers->removeRef (receiver); +} + +/** + * \brief Connect a receiver to the emitter. + * + * This is protected, a sub class should define a wrapper, with the respective + * receiver as an argument, to gain type safety. + */ +void Emitter::connect (Receiver *receiver) +{ + receivers->append (receiver); + receiver->connectTo (this); +} + +/** + * \brief Emit a void signal. + * + * This method should be called by a wrapper (return value void), which + * \em folds the signal, and delegates the emission to here. + */ +void Emitter::emitVoid (int signalNo, int argc, Object **argv) +{ + for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext(); + emitToReceiver (receiver, signalNo, argc, argv); + } +} + +/** + * \brief Emit a boolean signal. + * + * This method should be called by a wrapper, which \em folds the signal, + * delegates the emission to here, and returns the same boolean value. + */ +bool Emitter::emitBool (int signalNo, int argc, Object **argv) +{ + bool b = false, bt; + + for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext(); + // Note: All receivers are called, even if one returns true. + // Therefore, something like + // b = b || emitToReceiver (receiver, signalNo, argc, argv); + // does not work. + bt = emitToReceiver (receiver, signalNo, argc, argv); + b = b || bt; + } + + return b; +} + + +// -------------- +// Receiver +// -------------- + +Receiver::Receiver() +{ + emitters = new List <Emitter> (false); +} + +Receiver::~Receiver() +{ + for (Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) { + Emitter *emitter = it.getNext(); + emitter->unconnect (this); + } + delete emitters; +} + +void Receiver::intoStringBuffer(misc::StringBuffer *sb) +{ + // emitters are not listed, to prevent recursion + sb->append ("<receiver>"); +} + +void Receiver::connectTo(Emitter *emitter) +{ + emitters->append (emitter); +} + +void Receiver::unconnectFrom(Emitter *emitter) +{ + emitters->removeRef (emitter); +} + +// ------------------------ +// ObservedObject +// ------------------------ + +bool ObservedObject::DeletionEmitter::emitToReceiver (Receiver *receiver, + int signalNo, + int argc, Object **argv) +{ + object::TypedPointer <ObservedObject> *p = + (object::TypedPointer<ObservedObject>*)argv[0]; + ((DeletionReceiver*)receiver)->deleted (p->getTypedValue ()); + return false; +} + +void ObservedObject::DeletionEmitter::emitDeletion (ObservedObject *obj) +{ + object::TypedPointer <ObservedObject> p(obj); + object::Object *argv[1] = { &p }; + emitVoid (0, 1, argv); +} + +ObservedObject::~ObservedObject() +{ + deletionEmitter.emitDeletion (this); +} + +} // namespace signal +} // namespace lout diff --git a/lout/signal.hh b/lout/signal.hh new file mode 100644 index 0000000..117779d --- /dev/null +++ b/lout/signal.hh @@ -0,0 +1,310 @@ +#ifndef __LOUT_SIGNALS_HH__ +#define __LOUT_SIGNALS_HH__ + +#include "object.hh" +#include "container.hh" + +namespace lout { + +/** + * \brief This namespace provides base classes to define signals. + * + * By using signals, objects may be connected at run-time, e.g. a general + * button widget may be connected to another application-specific object, + * which reacts on the operations on the button by the user. In this case, + * the button e.g. defines a signal "clicked", which is "emitted" each + * time the user clicks on the button. After the application-specific + * object has been connected to this signal, a specific method of it will + * be called each time, this button emits the "clicked" signal. + * + * Below, we will call the level, on which signals are defined, the + * "general level", and the level, on which the signals are connected, + * the "caller level". + * + * <h3>Defining Signals</h3> + * + * Typically, signals are grouped. To define a signal group \em bar for your + * class \em Foo, you have to define two classes, the emitter and the + * receiver (BarEmitter and BarReceiver), and instantiate the emitter: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_signal { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="signal"; + * + * Emitter [color="#a0a0a0", URL="\ref signal::Emitter"]; + * Receiver [color="#a0a0a0", URL="\ref signal::Receiver"]; + * } + * + * subgraph cluster_foo { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="General (foo)"; + * + * Foo; + * BarEmitter; + * BarReceiver [color="#a0a0a0"]; + * } + * + * Emitter -> BarEmitter; + * Receiver -> BarReceiver; + * Foo -> BarEmitter [arrowhead="open", arrowtail="none", + * headlabel="1", taillabel="1"]; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * BarEmitter (class and instance) may be kept private, but BarReceiver must + * be public, since the caller of Foo must create a sub class of it. For + * BarEmitter, several methods must be implemented, see signal::Emitter for + * details. In BarReceiver, only some virtual abstract methods are defined, + * which the caller must implement. In this case, it is recommended to define + * a connectBar(BarReceiver*) method in Foo, which is delegated to the + * BarEmitter. + * + * <h3>Connecting to Signals</h3> + * + * A caller, which wants to connect to a signal, must define a sub class of + * the receiver, and implement the virtual methods. A typical design looks + * like this: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_foo { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="Generall (foo)"; + * + * BarReceiver [color="#a0a0a0"]; + * } + * + * subgraph cluster_qix { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="Caller (qix)"; + * + * Qix; + * QixBarReceiver; + * } + * + * BarReceiver -> QixBarReceiver [arrowhead="none", arrowtail="empty"]; + * QixBarReceiver -> Qix [headlabel="1", taillabel="*"]; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * (We skip "baz" in the canon, for better readability.) + * + * Here, the QixBarReceiver is connected to the Qix, so that the signals can + * be delegated to the Qix. Notice that the receiver gets automatically + * disconnected, when deleted (see signal::Receiver::~Receiver). + * + * <h3>Void and Boolean Signals</h3> + * + * In the simplest case, signal emitting involves calling a list of + * signal receivers (void signals). For boolean signals, the receivers return + * a boolean value, and the result of the signal emission (the return value of + * signal::Emitter::emitBool) returns the disjunction of the values returned + * by the receivers. Typically, a receiver states with its return value, + * whether the signal was used in any way, the resulting return value so + * indicates, whether at least one receiver has used the signal. + * + * In Dw, events are processed this way. In the simplest case, they are + * delegated to the parent widget, if the widget does not process them (by + * returning false). As an addition, signals are emitted, and if a receiver + * processes the event, this is handled the same way, as if the widget itself + * would have processed it. + * + * Notice, that also for boolean signals, all receivers are called, even + * after one receiver has already returned true. + * + * <h3>Memory Management</h3> + * + * <h4>Emitters</h4> + * + * Emitters are typically instantiated one, for one object emitting the + * signals. In the example above, the class Foo will contain a field + * "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter"). + * + * <h4>Receivers</h4> + * + * It is important, that a emitter never deletes a receiver, it just removes + * them from the receivers list. Likewise, when a receiver is deleted, it + * unconnects itself from all emitters. (The same receiver instance can indeed + * be connected to multiple emitters.) So, the caller has to care about + * deleting receivers. + * + * In the example above, something like that will work: + * + * \code + * class Qix + * { + * private: + * class QixBarReceiver + * { + * public: + * Qix *qix; + * // ... + * }; + * + * QixBarReceiver barReceiver; + * + * // ... + * }; + * \endcode + * + * The constructor of Qix should then set \em qix: + * + * \code + * Qix::Qix () + * { + * barReceiver.qix = this. + * // ... + * } + * \endcode + * + * After this, &\em barReceiver can be connected to all instances of + * BarEmitter, also multiple times. + */ +namespace signal { + +class Receiver; + +/** + * \brief The base class for signal emitters. + * + * If defining a signal group, a sub class of this class must be defined, + * with + * + * <ul> + * <li> a definition of the different signals (as enumeration), + * <li> an implementation of signal::Emitter::emitToReceiver, + * <li> wrappers for signal::Emitter::emitVoid and signal::Emitter::emitBool, + * respectively (one for each signal), and + * <li> a wrapper for signal::Emitter::connect. + * </ul> + * + * There are two representations of signals: + * + * <ul> + * <li> In the \em unfolded representation, the signal itself is represented + * by the method itself (in the emitter or the receiver), and the + * arguments are represented as normal C++ types. + * + * <li> \em Folding signals means to represent the signal itself by an integer + * number (enumeration), and translate the arguments in an object::Object* + * array. (If a given argument is not an instance of object::Object*, + * the wrappers in ::object can be used.) + * </ul> + * + * \sa ::signal + */ +class Emitter: public object::Object +{ + friend class Receiver; + +private: + container::typed::List <Receiver> *receivers; + + void unconnect (Receiver *receiver); + +protected: + void emitVoid (int signalNo, int argc, Object **argv); + bool emitBool (int signalNo, int argc, Object **argv); + void connect(Receiver *receiver); + + /** + * \brief A sub class must implement this for a call to a single + * receiver. + * + * This methods gets the signal in a \em folded representation, it has + * to unfold it, and pass it to a single receiver. For boolean signals, + * the return value of the receiver must be returned, for void signals, + * the return value is discarded. + */ + virtual bool emitToReceiver (Receiver *receiver, int signalNo, + int argc, Object **argv) = 0; + +public: + Emitter(); + ~Emitter(); + + void intoStringBuffer(misc::StringBuffer *sb); +}; + +/** + * \brief The base class for signal receiver base classes. + * + * If defining a signal group, a sub class of this class must be defined, + * in which only the abstract signal methods must be defined. + * + * \sa ::signal + */ +class Receiver: public object::Object +{ + friend class Emitter; + +private: + container::typed::List<Emitter> *emitters; + + void connectTo(Emitter *emitter); + void unconnectFrom(Emitter *emitter); + +public: + Receiver(); + ~Receiver(); + + void intoStringBuffer(misc::StringBuffer *sb); +}; + +/** + * \brief An observed object has a signal emitter, which tells the + * receivers, when the object is deleted. + */ +class ObservedObject +{ +public: + class DeletionReceiver: public signal::Receiver + { + public: + virtual void deleted (ObservedObject *object) = 0; + }; + +private: + class DeletionEmitter: public signal::Emitter + { + protected: + bool emitToReceiver (signal::Receiver *receiver, int signalNo, + int argc, Object **argv); + + public: + inline void connectDeletion (DeletionReceiver *receiver) + { connect (receiver); } + + void emitDeletion (ObservedObject *obj); + }; + + DeletionEmitter deletionEmitter; + +public: + virtual ~ObservedObject(); + + inline void connectDeletion (DeletionReceiver *receiver) + { deletionEmitter.connectDeletion (receiver); } +}; + +} // namespace signal + +} // namespace lout + +#endif // __LOUT_SIGNALS_HH__ diff --git a/lout/unicode.cc b/lout/unicode.cc new file mode 100644 index 0000000..da9c27a --- /dev/null +++ b/lout/unicode.cc @@ -0,0 +1,190 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2012, 2013 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + +#include "unicode.hh" +#include "misc.hh" + +using namespace lout::misc; + +namespace lout { + +namespace unicode { + +static unsigned char alpha[0x500] = { + // 0000-007F: C0 Controls and Basic Latin + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0x07, + // 0080-00FF: C1 Controls and Latin-1 Supplement + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, + // 0100-017F: Latin Extended-A + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // 0180-024F: Latin Extended-B + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, + // 0250–02AF: IPA Extensions + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + // 02B0–02FF: Spacing Modifier Letters + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + // 0300–036F: Combining Diacritical Marks + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0370–03FF: Greek and Coptic + 0xcf, 0x00, 0x40, 0x7d, 0xff, 0xff, 0xfb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + // 0400–04FF: Cyrillic + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x03, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/** + * Returns whether a given unicode character is an alphabetic character. + */ +bool isAlpha (int ch) +{ + return ch < 0x500 && (alpha[ch / 8] & (1 << (ch & 7))); +} + +int decodeUtf8 (const char *s) +{ + if((s[0] & 0x80) == 0) + return s[0]; + else if((s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80) + return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f); + else if((s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80) + return ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); + else if((s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) + return ((s[0] & 0x0f) << 18) | ((s[1] & 0x3f) << 12) + | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); + else + // Treat as ISO-8859-1 / ISO-8859-15 / Windows-1252 + return s[0]; +} + + +int decodeUtf8 (const char *s, int len) +{ + if(len >= 1 && (s[0] & 0x80) == 0) + return s[0]; + else if(len >= 2 && (s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80) + return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f); + else if(len >= 3 && (s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80) + return ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); + else if(len >= 4 && (s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) + return ((s[0] & 0x0f) << 18) | ((s[1] & 0x3f) << 12) + | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); + else + // Treat as ISO-8859-1 / ISO-8859-15 / Windows-1252 + return s[0]; +} + +const char *nextUtf8Char (const char *s) +{ + const char *r; + + if (s == NULL || s[0] == 0) + r = NULL; + else if((s[0] & 0x80) == 0) + r = s + 1; + else if((s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80) + r = s + 2; + else if((s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80) + r = s + 3; + else if((s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) + r = s + 4; + else + // invalid UTF-8 sequence: treat as one byte. + r = s + 1; + + if (r && r[0] == 0) + return NULL; + else + return r; +} + +const char *nextUtf8Char (const char *s, int len) +{ + const char *r; + + if (s == NULL || len <= 0) + r = NULL; + else if(len >= 1 && (s[0] & 0x80) == 0) + r = s + 1; + else if(len >= 2 && (s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80) + r = s + 2; + else if(len >= 3 && (s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80) + r = s + 3; + else if(len >= 4 && (s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 + && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) + r = s + 4; + else + // invalid UTF-8 sequence: treat as one byte. + r = s + 1; + + if (r && r - s >= len) + return NULL; + else + return r; +} + +int numUtf8Chars (const char *s) +{ + int numUtf8 = 0; + for (const char *r = s; r; r = nextUtf8Char (r)) + numUtf8++; + return numUtf8; +} + +int numUtf8Chars (const char *s, int len) +{ + int numUtf8 = 0; + for (const char *r = s; len > 0 && r; r = nextUtf8Char (r, len)) + numUtf8++; + return numUtf8; +} + +} // namespace lout + +} // namespace unicode diff --git a/lout/unicode.hh b/lout/unicode.hh new file mode 100644 index 0000000..c74fd9b --- /dev/null +++ b/lout/unicode.hh @@ -0,0 +1,30 @@ +#ifndef __UNICODE_HH__ +#define __UNICODE_HH__ + +namespace lout { + +/** + * \brief Stuff dealing with Unicode characters: UTF-8, character classes etc. + * + */ +namespace unicode { + +bool isAlpha (int ch); + +int decodeUtf8 (const char *s); + +int decodeUtf8 (const char *s, int len); + +const char *nextUtf8Char (const char *s); + +const char *nextUtf8Char (const char *s, int len); + +int numUtf8Chars (const char *s); + +int numUtf8Chars (const char *s, int len); + +} // namespace lout + +} // namespace unicode + +#endif // __UNICODE_HH__ diff --git a/ltmain.sh b/ltmain.sh new file mode 100644 index 0000000..147d758 --- /dev/null +++ b/ltmain.sh @@ -0,0 +1,11156 @@ +#! /bin/sh +## DO NOT EDIT - This file generated from ./build-aux/ltmain.in +## by inline-source v2014-01-03.01 + +# libtool (GNU libtool) 2.4.6 +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 + +# Copyright (C) 1996-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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, see <http://www.gnu.org/licenses/>. + + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.6 Debian-2.4.6-0.1" +package_revision=2.4.6 + + +## ------ ## +## Usage. ## +## ------ ## + +# Run './libtool --help' for help with using this script from the +# command line. + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# After configure completes, it has a better idea of some of the +# shell tools we need than the defaults used by the functions shared +# with bootstrap, so set those here where they can still be over- +# ridden by the user, but otherwise take precedence. + +: ${AUTOCONF="autoconf"} +: ${AUTOMAKE="automake"} + + +## -------------------------- ## +## Source external libraries. ## +## -------------------------- ## + +# Much of our low-level functionality needs to be sourced from external +# libraries, which are installed to $pkgauxdir. + +# Set a version string for this script. +scriptversion=2015-01-20.17; # UTC + +# General shell script boiler plate, and helper functions. +# Written by Gary V. Vaughan, 2004 + +# Copyright (C) 2004-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# 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 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES 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, see <http://www.gnu.org/licenses/>. + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Evaluate this file near the top of your script to gain access to +# the functions and variables defined here: +# +# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh +# +# If you need to override any of the default environment variable +# settings, do that before evaluating this file. + + +## -------------------- ## +## Shell normalisation. ## +## -------------------- ## + +# Some shells need a little help to be as Bourne compatible as possible. +# Before doing anything else, make sure all that help has been provided! + +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac +fi + +# NLS nuisances: We save the old values in case they are required later. +_G_user_locale= +_G_safe_locale= +for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test set = \"\${$_G_var+set}\"; then + save_$_G_var=\$$_G_var + $_G_var=C + export $_G_var + _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" + _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" + fi" +done + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Make sure IFS has a sensible default +sp=' ' +nl=' +' +IFS="$sp $nl" + +# There are apparently some retarded systems that use ';' as a PATH separator! +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + + +## ------------------------- ## +## Locate command utilities. ## +## ------------------------- ## + + +# func_executable_p FILE +# ---------------------- +# Check that FILE is an executable regular file. +func_executable_p () +{ + test -f "$1" && test -x "$1" +} + + +# func_path_progs PROGS_LIST CHECK_FUNC [PATH] +# -------------------------------------------- +# Search for either a program that responds to --version with output +# containing "GNU", or else returned by CHECK_FUNC otherwise, by +# trying all the directories in PATH with each of the elements of +# PROGS_LIST. +# +# CHECK_FUNC should accept the path to a candidate program, and +# set $func_check_prog_result if it truncates its output less than +# $_G_path_prog_max characters. +func_path_progs () +{ + _G_progs_list=$1 + _G_check_func=$2 + _G_PATH=${3-"$PATH"} + + _G_path_prog_max=0 + _G_path_prog_found=false + _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} + for _G_dir in $_G_PATH; do + IFS=$_G_save_IFS + test -z "$_G_dir" && _G_dir=. + for _G_prog_name in $_G_progs_list; do + for _exeext in '' .EXE; do + _G_path_prog=$_G_dir/$_G_prog_name$_exeext + func_executable_p "$_G_path_prog" || continue + case `"$_G_path_prog" --version 2>&1` in + *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; + *) $_G_check_func $_G_path_prog + func_path_progs_result=$func_check_prog_result + ;; + esac + $_G_path_prog_found && break 3 + done + done + done + IFS=$_G_save_IFS + test -z "$func_path_progs_result" && { + echo "no acceptable sed could be found in \$PATH" >&2 + exit 1 + } +} + + +# We want to be able to use the functions in this file before configure +# has figured out where the best binaries are kept, which means we have +# to search for them ourselves - except when the results are already set +# where we skip the searches. + +# Unless the user overrides by setting SED, search the path for either GNU +# sed, or the sed that truncates its output the least. +test -z "$SED" && { + _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for _G_i in 1 2 3 4 5 6 7; do + _G_sed_script=$_G_sed_script$nl$_G_sed_script + done + echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed + _G_sed_script= + + func_check_prog_sed () + { + _G_path_prog=$1 + + _G_count=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo '' >> conftest.nl + "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + rm -f conftest.sed + SED=$func_path_progs_result +} + + +# Unless the user overrides by setting GREP, search the path for either GNU +# grep, or the grep that truncates its output the least. +test -z "$GREP" && { + func_check_prog_grep () + { + _G_path_prog=$1 + + _G_count=0 + _G_path_prog_max=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo 'GREP' >> conftest.nl + "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + GREP=$func_path_progs_result +} + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase variable names are used for environment variables. These +# variables can be overridden by the user before calling a script that +# uses them if a suitable command of that name is not already available +# in the command search PATH. + +: ${CP="cp -f"} +: ${ECHO="printf %s\n"} +: ${EGREP="$GREP -E"} +: ${FGREP="$GREP -F"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} + + +## -------------------- ## +## Useful sed snippets. ## +## -------------------- ## + +sed_dirname='s|/[^/]*$||' +sed_basename='s|^.*/||' + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Same as above, but do not quote variable references. +sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' + +# Sed substitution that converts a w32 file name or path +# that contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-'\' parameter expansions in output of sed_double_quote_subst that +# were '\'-ed in input to the same. If an odd number of '\' preceded a +# '$' in input to sed_double_quote_subst, that '$' was protected from +# expansion. Since each input '\' is now two '\'s, look for any number +# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. +_G_bs='\\' +_G_bs2='\\\\' +_G_bs4='\\\\\\\\' +_G_dollar='\$' +sed_double_backslash="\ + s/$_G_bs4/&\\ +/g + s/^$_G_bs2$_G_dollar/$_G_bs&/ + s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g + s/\n//g" + + +## ----------------- ## +## Global variables. ## +## ----------------- ## + +# Except for the global variables explicitly listed below, the following +# functions in the '^func_' namespace, and the '^require_' namespace +# variables initialised in the 'Resource management' section, sourcing +# this file will not pollute your global namespace with anything +# else. There's no portable way to scope variables in Bourne shell +# though, so actually running these functions will sometimes place +# results into a variable named after the function, and often use +# temporary variables in the '^_G_' namespace. If you are careful to +# avoid using those namespaces casually in your sourcing script, things +# should continue to work as you expect. And, of course, you can freely +# overwrite any of the functions or variables defined here before +# calling anything to customize them. + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +# Allow overriding, eg assuming that you follow the convention of +# putting '$debug_cmd' at the start of all your functions, you can get +# bash to show function call trace with: +# +# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name +debug_cmd=${debug_cmd-":"} +exit_cmd=: + +# By convention, finish your script with: +# +# exit $exit_status +# +# so that you can set exit_status to non-zero if you want to indicate +# something went wrong during execution without actually bailing out at +# the point of failure. +exit_status=$EXIT_SUCCESS + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath=$0 + +# The name of this program. +progname=`$ECHO "$progpath" |$SED "$sed_basename"` + +# Make sure we have an absolute progpath for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` + progdir=`cd "$progdir" && pwd` + progpath=$progdir/$progname + ;; + *) + _G_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS=$_G_IFS + test -x "$progdir/$progname" && break + done + IFS=$_G_IFS + test -n "$progdir" || progdir=`pwd` + progpath=$progdir/$progname + ;; +esac + + +## ----------------- ## +## Standard options. ## +## ----------------- ## + +# The following options affect the operation of the functions defined +# below, and should be set appropriately depending on run-time para- +# meters passed on the command line. + +opt_dry_run=false +opt_quiet=false +opt_verbose=false + +# Categories 'all' and 'none' are always available. Append any others +# you will pass as the first argument to func_warning from your own +# code. +warning_categories= + +# By default, display warnings according to 'opt_warning_types'. Set +# 'warning_func' to ':' to elide all warnings, or func_fatal_error to +# treat the next displayed warning as a fatal error. +warning_func=func_warn_and_continue + +# Set to 'all' to display all warnings, 'none' to suppress all +# warnings, or a space delimited list of some subset of +# 'warning_categories' to display only the listed warnings. +opt_warning_types=all + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Call them using their associated +# 'require_*' variable to ensure that they are executed, at most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_term_colors +# ------------------- +# Allow display of bold text on terminals that support it. +require_term_colors=func_require_term_colors +func_require_term_colors () +{ + $debug_cmd + + test -t 1 && { + # COLORTERM and USE_ANSI_COLORS environment variables take + # precedence, because most terminfo databases neglect to describe + # whether color sequences are supported. + test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} + + if test 1 = "$USE_ANSI_COLORS"; then + # Standard ANSI escape sequences + tc_reset='[0m' + tc_bold='[1m'; tc_standout='[7m' + tc_red='[31m'; tc_green='[32m' + tc_blue='[34m'; tc_cyan='[36m' + else + # Otherwise trust the terminfo database after all. + test -n "`tput sgr0 2>/dev/null`" && { + tc_reset=`tput sgr0` + test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` + tc_standout=$tc_bold + test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` + test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` + test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` + test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` + test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` + } + fi + } + + require_term_colors=: +} + + +## ----------------- ## +## Function library. ## +## ----------------- ## + +# This section contains a variety of useful functions to call in your +# scripts. Take note of the portable wrappers for features provided by +# some modern shells, which will fall back to slower equivalents on +# less featureful shells. + + +# func_append VAR VALUE +# --------------------- +# Append VALUE onto the existing contents of VAR. + + # We should try to minimise forks, especially on Windows where they are + # unreasonably slow, so skip the feature probes when bash or zsh are + # being used: + if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then + : ${_G_HAVE_ARITH_OP="yes"} + : ${_G_HAVE_XSI_OPS="yes"} + # The += operator was introduced in bash 3.1 + case $BASH_VERSION in + [12].* | 3.0 | 3.0*) ;; + *) + : ${_G_HAVE_PLUSEQ_OP="yes"} + ;; + esac + fi + + # _G_HAVE_PLUSEQ_OP + # Can be empty, in which case the shell is probed, "yes" if += is + # useable or anything else if it does not work. + test -z "$_G_HAVE_PLUSEQ_OP" \ + && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ + && _G_HAVE_PLUSEQ_OP=yes + +if test yes = "$_G_HAVE_PLUSEQ_OP" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_append () + { + $debug_cmd + + eval "$1+=\$2" + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_append () + { + $debug_cmd + + eval "$1=\$$1\$2" + } +fi + + +# func_append_quoted VAR VALUE +# ---------------------------- +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +if test yes = "$_G_HAVE_PLUSEQ_OP"; then + eval 'func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" + }' +else + func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" + } +fi + + +# func_append_uniq VAR VALUE +# -------------------------- +# Append unique VALUE onto the existing contents of VAR, assuming +# entries are delimited by the first character of VALUE. For example: +# +# func_append_uniq options " --another-option option-argument" +# +# will only append to $options if " --another-option option-argument " +# is not already present somewhere in $options already (note spaces at +# each end implied by leading space in second argument). +func_append_uniq () +{ + $debug_cmd + + eval _G_current_value='`$ECHO $'$1'`' + _G_delim=`expr "$2" : '\(.\)'` + + case $_G_delim$_G_current_value$_G_delim in + *"$2$_G_delim"*) ;; + *) func_append "$@" ;; + esac +} + + +# func_arith TERM... +# ------------------ +# Set func_arith_result to the result of evaluating TERMs. + test -z "$_G_HAVE_ARITH_OP" \ + && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ + && _G_HAVE_ARITH_OP=yes + +if test yes = "$_G_HAVE_ARITH_OP"; then + eval 'func_arith () + { + $debug_cmd + + func_arith_result=$(( $* )) + }' +else + func_arith () + { + $debug_cmd + + func_arith_result=`expr "$@"` + } +fi + + +# func_basename FILE +# ------------------ +# Set func_basename_result to FILE with everything up to and including +# the last / stripped. +if test yes = "$_G_HAVE_XSI_OPS"; then + # If this shell supports suffix pattern removal, then use it to avoid + # forking. Hide the definitions single quotes in case the shell chokes + # on unsupported syntax... + _b='func_basename_result=${1##*/}' + _d='case $1 in + */*) func_dirname_result=${1%/*}$2 ;; + * ) func_dirname_result=$3 ;; + esac' + +else + # ...otherwise fall back to using sed. + _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' + _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` + if test "X$func_dirname_result" = "X$1"; then + func_dirname_result=$3 + else + func_append func_dirname_result "$2" + fi' +fi + +eval 'func_basename () +{ + $debug_cmd + + '"$_b"' +}' + + +# func_dirname FILE APPEND NONDIR_REPLACEMENT +# ------------------------------------------- +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +eval 'func_dirname () +{ + $debug_cmd + + '"$_d"' +}' + + +# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT +# -------------------------------------------------------- +# Perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# For efficiency, we do not delegate to the functions above but instead +# duplicate the functionality here. +eval 'func_dirname_and_basename () +{ + $debug_cmd + + '"$_b"' + '"$_d"' +}' + + +# func_echo ARG... +# ---------------- +# Echo program name prefixed message. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_echo_all ARG... +# -------------------- +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + + +# func_echo_infix_1 INFIX ARG... +# ------------------------------ +# Echo program name, followed by INFIX on the first line, with any +# additional lines not showing INFIX. +func_echo_infix_1 () +{ + $debug_cmd + + $require_term_colors + + _G_infix=$1; shift + _G_indent=$_G_infix + _G_prefix="$progname: $_G_infix: " + _G_message=$* + + # Strip color escape sequences before counting printable length + for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" + do + test -n "$_G_tc" && { + _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` + _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` + } + done + _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes + + func_echo_infix_1_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_infix_1_IFS + $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 + _G_prefix=$_G_indent + done + IFS=$func_echo_infix_1_IFS +} + + +# func_error ARG... +# ----------------- +# Echo program name prefixed message to standard error. +func_error () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 +} + + +# func_fatal_error ARG... +# ----------------------- +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + $debug_cmd + + func_error "$*" + exit $EXIT_FAILURE +} + + +# func_grep EXPRESSION FILENAME +# ----------------------------- +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $debug_cmd + + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_len STRING +# --------------- +# Set func_len_result to the length of STRING. STRING may not +# start with a hyphen. + test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` + } +fi + + +# func_mkdir_p DIRECTORY-PATH +# --------------------------- +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + $debug_cmd + + _G_directory_path=$1 + _G_dir_list= + + if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then + + # Protect directory names starting with '-' + case $_G_directory_path in + -*) _G_directory_path=./$_G_directory_path ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$_G_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + _G_dir_list=$_G_directory_path:$_G_dir_list + + # If the last portion added has no slash in it, the list is done + case $_G_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` + done + _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` + + func_mkdir_p_IFS=$IFS; IFS=: + for _G_dir in $_G_dir_list; do + IFS=$func_mkdir_p_IFS + # mkdir can fail with a 'File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$_G_dir" 2>/dev/null || : + done + IFS=$func_mkdir_p_IFS + + # Bail out if we (or some other process) failed to create a directory. + test -d "$_G_directory_path" || \ + func_fatal_error "Failed to create '$1'" + fi +} + + +# func_mktempdir [BASENAME] +# ------------------------- +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, BASENAME is the basename for that directory. +func_mktempdir () +{ + $debug_cmd + + _G_template=${TMPDIR-/tmp}/${1-$progname} + + if test : = "$opt_dry_run"; then + # Return a directory name, but don't create it in dry-run mode + _G_tmpdir=$_G_template-$$ + else + + # If mktemp works, use that first and foremost + _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` + + if test ! -d "$_G_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + _G_tmpdir=$_G_template-${RANDOM-0}$$ + + func_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$_G_tmpdir" + umask $func_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$_G_tmpdir" || \ + func_fatal_error "cannot create temporary directory '$_G_tmpdir'" + fi + + $ECHO "$_G_tmpdir" +} + + +# func_normal_abspath PATH +# ------------------------ +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +func_normal_abspath () +{ + $debug_cmd + + # These SED scripts presuppose an absolute path with a trailing slash. + _G_pathcar='s|^/\([^/]*\).*$|\1|' + _G_pathcdr='s|^/[^/]*||' + _G_removedotparts=':dotsl + s|/\./|/|g + t dotsl + s|/\.$|/|' + _G_collapseslashes='s|/\{1,\}|/|g' + _G_finalslash='s|/*$|/|' + + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` + while :; do + # Processed it all yet? + if test / = "$func_normal_abspath_tpath"; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result"; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + + +# func_notquiet ARG... +# -------------------- +# Echo program name prefixed message only when not in quiet mode. +func_notquiet () +{ + $debug_cmd + + $opt_quiet || func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + + +# func_relative_path SRCDIR DSTDIR +# -------------------------------- +# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. +func_relative_path () +{ + $debug_cmd + + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=$func_dirname_result + if test -z "$func_relative_path_tlibdir"; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test -n "$func_stripname_result"; then + func_append func_relative_path_result "/$func_stripname_result" + fi + + # Normalisation. If bindir is libdir, return '.' else relative path. + if test -n "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + fi + + test -n "$func_relative_path_result" || func_relative_path_result=. + + : +} + + +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. +func_quote_for_eval () +{ + $debug_cmd + + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in + *[\\\`\"\$]*) + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi + + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" + ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; + esac + + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done +} + + +# func_quote_for_expand ARG +# ------------------------- +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + $debug_cmd + + case $1 in + *[\\\`\"]*) + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; + *) + _G_arg=$1 ;; + esac + + case $_G_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_arg=\"$_G_arg\" + ;; + esac + + func_quote_for_expand_result=$_G_arg +} + + +# func_stripname PREFIX SUFFIX NAME +# --------------------------------- +# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_stripname () + { + $debug_cmd + + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary variable first. + func_stripname_result=$3 + func_stripname_result=${func_stripname_result#"$1"} + func_stripname_result=${func_stripname_result%"$2"} + }' +else + func_stripname () + { + $debug_cmd + + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; + esac + } +fi + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" + + $opt_dry_run || { + eval "$_G_cmd" + _G_status=$? + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_show_eval_locale CMD [FAIL_EXP] +# ------------------------------------ +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + $opt_quiet || { + func_quote_for_expand "$_G_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + $opt_dry_run || { + eval "$_G_user_locale + $_G_cmd" + _G_status=$? + eval "$_G_safe_locale" + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_tr_sh +# ---------- +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + $debug_cmd + + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_verbose ARG... +# ------------------- +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $debug_cmd + + $opt_verbose && func_echo "$*" + + : +} + + +# func_warn_and_continue ARG... +# ----------------------------- +# Echo program name prefixed warning message to standard error. +func_warn_and_continue () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 +} + + +# func_warning CATEGORY ARG... +# ---------------------------- +# Echo program name prefixed warning message to standard error. Warning +# messages can be filtered according to CATEGORY, where this function +# elides messages where CATEGORY is not listed in the global variable +# 'opt_warning_types'. +func_warning () +{ + $debug_cmd + + # CATEGORY must be in the warning_categories list! + case " $warning_categories " in + *" $1 "*) ;; + *) func_internal_error "invalid warning category '$1'" ;; + esac + + _G_category=$1 + shift + + case " $opt_warning_types " in + *" $_G_category "*) $warning_func ${1+"$@"} ;; + esac +} + + +# func_sort_ver VER1 VER2 +# ----------------------- +# 'sort -V' is not generally available. +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +func_sort_ver () +{ + $debug_cmd + + printf '%s\n%s\n' "$1" "$2" \ + | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n +} + +# func_lt_ver PREV CURR +# --------------------- +# Return true if PREV and CURR are in the correct order according to +# func_sort_ver, otherwise false. Use it like this: +# +# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." +func_lt_ver () +{ + $debug_cmd + + test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Set a version string for this script. +scriptversion=2014-01-07.03; # UTC + +# A portable, pluggable option parser for Bourne shell. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# This file is a library for parsing options in your shell scripts along +# with assorted other useful supporting features that you can make use +# of too. +# +# For the simplest scripts you might need only: +# +# #!/bin/sh +# . relative/path/to/funclib.sh +# . relative/path/to/options-parser +# scriptversion=1.0 +# func_options ${1+"$@"} +# eval set dummy "$func_options_result"; shift +# ...rest of your script... +# +# In order for the '--version' option to work, you will need to have a +# suitably formatted comment like the one at the top of this file +# starting with '# Written by ' and ending with '# warranty; '. +# +# For '-h' and '--help' to work, you will also need a one line +# description of your script's purpose in a comment directly above the +# '# Written by ' line, like the one at the top of this file. +# +# The default options also support '--debug', which will turn on shell +# execution tracing (see the comment above debug_cmd below for another +# use), and '--verbose' and the func_verbose function to allow your script +# to display verbose messages only when your user has specified +# '--verbose'. +# +# After sourcing this file, you can plug processing for additional +# options by amending the variables from the 'Configuration' section +# below, and following the instructions in the 'Option parsing' +# section further down. + +## -------------- ## +## Configuration. ## +## -------------- ## + +# You should override these variables in your script after sourcing this +# file so that they reflect the customisations you have added to the +# option parser. + +# The usage line for option parsing errors and the start of '-h' and +# '--help' output messages. You can embed shell variables for delayed +# expansion at the time the message is displayed, but you will need to +# quote other shell meta-characters carefully to prevent them being +# expanded when the contents are evaled. +usage='$progpath [OPTION]...' + +# Short help message in response to '-h' and '--help'. Add to this or +# override it after sourcing this library to reflect the full set of +# options your script accepts. +usage_message="\ + --debug enable verbose shell tracing + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -v, --verbose verbosely report processing + --version print version information and exit + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=" +Warning categories include: + 'all' show all warnings + 'none' turn off all the warnings + 'error' warnings are treated as fatal errors" + +# Help message printed before fatal option parsing errors. +fatal_help="Try '\$progname --help' for more information." + + + +## ------------------------- ## +## Hook function management. ## +## ------------------------- ## + +# This section contains functions for adding, removing, and running hooks +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. + +# func_hookable FUNC_NAME +# ----------------------- +# Declare that FUNC_NAME will run hooks added with +# 'func_add_hook FUNC_NAME ...'. +func_hookable () +{ + $debug_cmd + + func_append hookable_fns " $1" +} + + +# func_add_hook FUNC_NAME HOOK_FUNC +# --------------------------------- +# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must +# first have been declared "hookable" by a call to 'func_hookable'. +func_add_hook () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not accept hook functions." ;; + esac + + eval func_append ${1}_hooks '" $2"' +} + + +# func_remove_hook FUNC_NAME HOOK_FUNC +# ------------------------------------ +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +func_remove_hook () +{ + $debug_cmd + + eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' +} + + +# func_run_hooks FUNC_NAME [ARG]... +# --------------------------------- +# Run all hook functions registered to FUNC_NAME. +# It is assumed that the list of hook functions contains nothing more +# than a whitespace-delimited list of legal shell function names, and +# no effort is wasted trying to catch shell meta-characters or preserve +# whitespace. +func_run_hooks () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; + esac + + eval _G_hook_fns=\$$1_hooks; shift + + for _G_hook in $_G_hook_fns; do + eval $_G_hook '"$@"' + + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift + done + + func_quote_for_eval ${1+"$@"} + func_run_hooks_result=$func_quote_for_eval_result +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# In order to add your own option parsing hooks, you must accept the +# full positional parameter list in your hook function, remove any +# options that you action, and then pass back the remaining unprocessed +# options in '<hooked_function_name>_result', escaped suitably for +# 'eval'. Like this: +# +# my_options_prep () +# { +# $debug_cmd +# +# # Extend the existing usage message. +# usage_message=$usage_message' +# -s, --silent don'\''t print informational messages +# ' +# +# func_quote_for_eval ${1+"$@"} +# my_options_prep_result=$func_quote_for_eval_result +# } +# func_add_hook func_options_prep my_options_prep +# +# +# my_silent_option () +# { +# $debug_cmd +# +# # Note that for efficiency, we parse as many options as we can +# # recognise in a loop before passing the remainder back to the +# # caller on the first unrecognised argument we encounter. +# while test $# -gt 0; do +# opt=$1; shift +# case $opt in +# --silent|-s) opt_silent=: ;; +# # Separate non-argument short options: +# -s*) func_split_short_opt "$_G_opt" +# set dummy "$func_split_short_opt_name" \ +# "-$func_split_short_opt_arg" ${1+"$@"} +# shift +# ;; +# *) set dummy "$_G_opt" "$*"; shift; break ;; +# esac +# done +# +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result +# } +# func_add_hook func_parse_options my_silent_option +# +# +# my_option_validation () +# { +# $debug_cmd +# +# $opt_silent && $opt_verbose && func_fatal_help "\ +# '--silent' and '--verbose' options are mutually exclusive." +# +# func_quote_for_eval ${1+"$@"} +# my_option_validation_result=$func_quote_for_eval_result +# } +# func_add_hook func_validate_options my_option_validation +# +# You'll alse need to manually amend $usage_message to reflect the extra +# options you parse. It's preferable to append if you can, so that +# multiple option parsing hooks can be added safely. + + +# func_options [ARG]... +# --------------------- +# All the functions called inside func_options are hookable. See the +# individual implementations for details. +func_hookable func_options +func_options () +{ + $debug_cmd + + func_options_prep ${1+"$@"} + eval func_parse_options \ + ${func_options_prep_result+"$func_options_prep_result"} + eval func_validate_options \ + ${func_parse_options_result+"$func_parse_options_result"} + + eval func_run_hooks func_options \ + ${func_validate_options_result+"$func_validate_options_result"} + + # save modified positional parameters for caller + func_options_result=$func_run_hooks_result +} + + +# func_options_prep [ARG]... +# -------------------------- +# All initialisations required before starting the option parse loop. +# Note that when calling hook functions, we pass through the list of +# positional parameters. If a hook function modifies that list, and +# needs to propogate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning. +func_hookable func_options_prep +func_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_verbose=false + opt_warning_types= + + func_run_hooks func_options_prep ${1+"$@"} + + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result +} + + +# func_parse_options [ARG]... +# --------------------------- +# The main option parsing loop. +func_hookable func_parse_options +func_parse_options () +{ + $debug_cmd + + func_parse_options_result= + + # this just eases exit handling + while test $# -gt 0; do + # Defer to hook functions for initial option parsing, so they + # get priority in the event of reusing an option name. + func_run_hooks func_parse_options ${1+"$@"} + + # Adjust func_parse_options positional parameters to match + eval set dummy "$func_run_hooks_result"; shift + + # Break out of the loop if we already parsed every option. + test $# -gt 0 || break + + _G_opt=$1 + shift + case $_G_opt in + --debug|-x) debug_cmd='set -x' + func_echo "enabling shell trace mode" + $debug_cmd + ;; + + --no-warnings|--no-warning|--no-warn) + set dummy --warnings none ${1+"$@"} + shift + ;; + + --warnings|--warning|-W) + test $# = 0 && func_missing_arg $_G_opt && break + case " $warning_categories $1" in + *" $1 "*) + # trailing space prevents matching last $1 above + func_append_uniq opt_warning_types " $1" + ;; + *all) + opt_warning_types=$warning_categories + ;; + *none) + opt_warning_types=none + warning_func=: + ;; + *error) + opt_warning_types=$warning_categories + warning_func=func_fatal_error + ;; + *) + func_fatal_error \ + "unsupported warning category: '$1'" + ;; + esac + shift + ;; + + --verbose|-v) opt_verbose=: ;; + --version) func_version ;; + -\?|-h) func_usage ;; + --help) func_help ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + # Separate optargs to short options: + -W*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-v*|-x*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result +} + + +# func_validate_options [ARG]... +# ------------------------------ +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +func_hookable func_validate_options +func_validate_options () +{ + $debug_cmd + + # Display all warnings if -W was not given. + test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" + + func_run_hooks func_validate_options ${1+"$@"} + + # Bail if the options were screwed! + $exit_cmd $EXIT_FAILURE + + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result +} + + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of the +# hookable option parser framework in ascii-betical order. + + +# func_fatal_help ARG... +# ---------------------- +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + eval \$ECHO \""$fatal_help"\" + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + + +# func_help +# --------- +# Echo long help message to standard output and exit. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message" + exit 0 +} + + +# func_missing_arg ARGNAME +# ------------------------ +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $debug_cmd + + func_error "Missing argument for '$1'." + exit_cmd=exit +} + + +# func_split_equals STRING +# ------------------------ +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. +test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=${1%%=*} + func_split_equals_rhs=${1#*=} + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` + func_split_equals_rhs= + test "x$func_split_equals_lhs" = "x$1" \ + || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` + } +fi #func_split_equals + + +# func_split_short_opt SHORTOPT +# ----------------------------- +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` + func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` + } +fi #func_split_short_opt + + +# func_usage +# ---------- +# Echo short help message to standard output and exit. +func_usage () +{ + $debug_cmd + + func_usage_message + $ECHO "Run '$progname --help |${PAGER-more}' for full usage" + exit 0 +} + + +# func_usage_message +# ------------------ +# Echo short help message to standard output. +func_usage_message () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + echo + $SED -n 's|^# || + /^Written by/{ + x;p;x + } + h + /^Written by/q' < "$progpath" + echo + eval \$ECHO \""$usage_message"\" +} + + +# func_version +# ------------ +# Echo version message to standard output and exit. +func_version () +{ + $debug_cmd + + printf '%s\n' "$progname $scriptversion" + $SED -n ' + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more + } + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p + } + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" + + exit $? +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: + +# Set a version string. +scriptversion='(GNU libtool) 2.4.6' + + +# func_echo ARG... +# ---------------- +# Libtool also displays the current mode in messages, so override +# funclib.sh func_echo with this custom definition. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_warning ARG... +# ------------------- +# Libtool warnings are not categorized, so override funclib.sh +# func_warning with this simpler definition. +func_warning () +{ + $debug_cmd + + $warning_func ${1+"$@"} +} + + +## ---------------- ## +## Options parsing. ## +## ---------------- ## + +# Hook in the functions to make sure our own options are parsed during +# the option parsing loop. + +usage='$progpath [OPTION]... [MODE-ARG]...' + +# Short help message in response to '-h'. +usage_message="Options: + --config show all configuration variables + --debug enable verbose shell tracing + -n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --mode=MODE use operation mode MODE + --no-warnings equivalent to '-Wnone' + --preserve-dup-deps don't remove duplicate dependency libraries + --quiet, --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + -v, --verbose print more informational messages than default + --version print version information + -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] + -h, --help, --help-all print short, long, or detailed help message +" + +# Additional text appended to 'usage_message' in response to '--help'. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. When passed as first option, +'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. +Try '$progname --help --mode=MODE' for a more detailed description of MODE. + +When reporting a bug, please describe a test case to reproduce it and +include the following information: + + host-triplet: $host + shell: $SHELL + compiler: $LTCC + compiler flags: $LTCFLAGS + linker: $LD (gnu? $with_gnu_ld) + version: $progname (GNU libtool) 2.4.6 + automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` + autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` + +Report bugs to <bug-libtool@gnu.org>. +GNU libtool home page: <http://www.gnu.org/s/libtool/>. +General help using GNU software: <http://www.gnu.org/gethelp/>." + exit 0 +} + + +# func_lo2o OBJECT-NAME +# --------------------- +# Transform OBJECT-NAME from a '.lo' suffix to the platform specific +# object suffix. + +lo2o=s/\\.lo\$/.$objext/ +o2lo=s/\\.$objext\$/.lo/ + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_lo2o () + { + case $1 in + *.lo) func_lo2o_result=${1%.lo}.$objext ;; + * ) func_lo2o_result=$1 ;; + esac + }' + + # func_xform LIBOBJ-OR-SOURCE + # --------------------------- + # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) + # suffix to a '.lo' libtool-object suffix. + eval 'func_xform () + { + func_xform_result=${1%.*}.lo + }' +else + # ...otherwise fall back to using sed. + func_lo2o () + { + func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` + } + + func_xform () + { + func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` + } +fi + + +# func_fatal_configuration ARG... +# ------------------------------- +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func__fatal_error ${1+"$@"} \ + "See the $PACKAGE documentation for more information." \ + "Fatal configuration error." +} + + +# func_config +# ----------- +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + + +# func_features +# ------------- +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test yes = "$build_libtool_libs"; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test yes = "$build_old_libs"; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + + +# func_enable_tag TAGNAME +# ----------------------- +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname=$1 + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf=/$re_begincf/,/$re_endcf/p + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + + +# func_check_version_match +# ------------------------ +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# libtool_options_prep [ARG]... +# ----------------------------- +# Preparation for options parsed by libtool. +libtool_options_prep () +{ + $debug_mode + + # Option defaults: + opt_config=false + opt_dlopen= + opt_dry_run=false + opt_help=false + opt_mode= + opt_preserve_dup_deps=false + opt_quiet=false + + nonopt= + preserve_args= + + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; + esac + + # Pass back the list of options. + func_quote_for_eval ${1+"$@"} + libtool_options_prep_result=$func_quote_for_eval_result +} +func_add_hook func_options_prep libtool_options_prep + + +# libtool_parse_options [ARG]... +# --------------------------------- +# Provide handling for libtool specific options. +libtool_parse_options () +{ + $debug_cmd + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_opt=$1 + shift + case $_G_opt in + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + + --config) func_config ;; + + --dlopen|-dlopen) + opt_dlopen="${opt_dlopen+$opt_dlopen +}$1" + shift + ;; + + --preserve-dup-deps) + opt_preserve_dup_deps=: ;; + + --features) func_features ;; + + --finish) set dummy --mode finish ${1+"$@"}; shift ;; + + --help) opt_help=: ;; + + --help-all) opt_help=': help-all' ;; + + --mode) test $# = 0 && func_missing_arg $_G_opt && break + opt_mode=$1 + case $1 in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $_G_opt" + exit_cmd=exit + break + ;; + esac + shift + ;; + + --no-silent|--no-quiet) + opt_quiet=false + func_append preserve_args " $_G_opt" + ;; + + --no-warnings|--no-warning|--no-warn) + opt_warning=false + func_append preserve_args " $_G_opt" + ;; + + --no-verbose) + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --silent|--quiet) + opt_quiet=: + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --tag) test $# = 0 && func_missing_arg $_G_opt && break + opt_tag=$1 + func_append preserve_args " $_G_opt $1" + func_enable_tag "$1" + shift + ;; + + --verbose|-v) opt_quiet=false + opt_verbose=: + func_append preserve_args " $_G_opt" + ;; + + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + libtool_parse_options_result=$func_quote_for_eval_result +} +func_add_hook func_parse_options libtool_parse_options + + + +# libtool_validate_options [ARG]... +# --------------------------------- +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +libtool_validate_options () +{ + # save first non-option argument + if test 0 -lt $#; then + nonopt=$1 + shift + fi + + # preserve --debug + test : = "$debug_cmd" || func_append preserve_args " --debug" + + case $host in + # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 + # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 + *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + test yes != "$build_libtool_libs" \ + && test yes != "$build_old_libs" \ + && func_fatal_configuration "not configured to build any kind of library" + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test execute != "$opt_mode"; then + func_error "unrecognized option '-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help=$help + help="Try '$progname --help --mode=$opt_mode' for more information." + } + + # Pass back the unparsed argument list + func_quote_for_eval ${1+"$@"} + libtool_validate_options_result=$func_quote_for_eval_result +} +func_add_hook func_validate_options libtool_validate_options + + +# Process options as early as possible so that --help and --version +# can return quickly. +func_options ${1+"$@"} +eval set dummy "$func_options_result"; shift + + + +## ----------- ## +## Main. ## +## ----------- ## + +magic='%%%MAGIC variable%%%' +magic_exe='%%%MAGIC EXE variable%%%' + +# Global variables. +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# func_generated_by_libtool +# True iff stdin has been generated by Libtool. This function is only +# a basic sanity check; it will hardly flush out determined imposters. +func_generated_by_libtool_p () +{ + $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if 'file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case $lalib_p_line in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test yes = "$lalib_p" +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + test -f "$1" && + $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $debug_cmd + + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# 'FILE.' does not work on cygwin managed mounts. +func_source () +{ + $debug_cmd + + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case $lt_sysroot:$1 in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result='='$func_stripname_result + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $debug_cmd + + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with '--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=$1 + if test yes = "$build_libtool_libs"; then + write_lobj=\'$2\' + else + write_lobj=none + fi + + if test yes = "$build_old_libs"; then + write_oldobj=\'$3\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <<EOF +# $write_libobj - a libtool object file +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object=$write_lobj + +# Name of the non-PIC object +non_pic_object=$write_oldobj + +EOF + $MV "${write_libobj}T" "$write_libobj" + } +} + + +################################################## +# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS # +################################################## + +# func_convert_core_file_wine_to_w32 ARG +# Helper function used by file name conversion functions when $build is *nix, +# and $host is mingw, cygwin, or some other w32 environment. Relies on a +# correctly configured wine environment available, with the winepath program +# in $build's $PATH. +# +# ARG is the $build file name to be converted to w32 format. +# Result is available in $func_convert_core_file_wine_to_w32_result, and will +# be empty on error (or when ARG is empty) +func_convert_core_file_wine_to_w32 () +{ + $debug_cmd + + func_convert_core_file_wine_to_w32_result=$1 + if test -n "$1"; then + # Unfortunately, winepath does not exit with a non-zero error code, so we + # are forced to check the contents of stdout. On the other hand, if the + # command is not found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both error code of + # zero AND non-empty stdout, which explains the odd construction: + func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $debug_cmd + + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result= + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result"; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $debug_cmd + + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $debug_cmd + + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $debug_cmd + + if test -z "$2" && test -n "$1"; then + func_error "Could not determine host file name corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result=$1 + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $debug_cmd + + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " '$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result=$3 + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $debug_cmd + + case $4 in + $1 ) func_to_host_path_result=$3$func_to_host_path_result + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via '$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $debug_cmd + + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $debug_cmd + + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result=$1 +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result=$func_convert_core_msys_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result=$func_convert_core_file_wine_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via '$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $debug_cmd + + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd=func_convert_path_$func_stripname_result + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $debug_cmd + + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result=$1 +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_msys_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_path_wine_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_dll_def_p FILE +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with _LT_DLL_DEF_P in libtool.m4 +func_dll_def_p () +{ + $debug_cmd + + func_dll_def_p_tmp=`$SED -n \ + -e 's/^[ ]*//' \ + -e '/^\(;.*\)*$/d' \ + -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ + -e q \ + "$1"` + test DEF = "$func_dll_def_p_tmp" +} + + +# func_mode_compile arg... +func_mode_compile () +{ + $debug_cmd + + # Get the compilation command and the source file. + base_compile= + srcfile=$nonopt # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg=$arg + arg_mode=normal + ;; + + target ) + libobj=$arg + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify '-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs=$IFS; IFS=, + for arg in $args; do + IFS=$save_ifs + func_append_quoted lastarg "$arg" + done + IFS=$save_ifs + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg=$srcfile + srcfile=$arg + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with '-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj=$func_basename_result + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from '$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test yes = "$build_libtool_libs" \ + || func_fatal_configuration "cannot build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name '$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname=$func_basename_result + xdir=$func_dirname_result + lobj=$xdir$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test yes = "$build_old_libs"; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test no = "$compiler_c_o"; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext + lockfile=$output_obj.lock + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test yes = "$need_locks"; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test warn = "$need_locks"; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test yes = "$build_libtool_libs"; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test no != "$pic_mode"; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test yes = "$suppress_opt"; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test yes = "$build_old_libs"; then + if test yes != "$pic_mode"; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test yes = "$compiler_c_o"; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test no != "$need_locks"; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test compile = "$opt_mode" && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a '.o' file suitable for static linking + -static only build a '.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a 'standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix '.c' with the +library object suffix, '.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to '-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the '--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the 'install' or 'cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE use a list of object files found in FILE to specify objects + -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with '-') are ignored. + +Every other argument is treated as a filename. Files ending in '.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in '.la', then a libtool library is created, +only library objects ('.lo' files) may be specified, and '-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created +using 'ar' and 'ranlib', or on Windows using 'lib'. + +If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode '$opt_mode'" + ;; + esac + + echo + $ECHO "Try '$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test : = "$opt_help"; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | $SED -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + $SED '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $debug_cmd + + # The first argument is the command name. + cmd=$nonopt + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "'$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "'$file' was not linked with '-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir=$func_dirname_result + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir=$func_dirname_result + ;; + + *) + func_warning "'-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir=$absdir + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic=$magic + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file=$progdir/$program + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file=$progdir/$program + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if $opt_dry_run; then + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + else + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd=\$cmd$args + fi +} + +test execute = "$opt_mode" && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $debug_cmd + + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "'$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument '$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and '=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_quiet && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the '-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the '$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the '$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the '$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test finish = "$opt_mode" && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $debug_cmd + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac + then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=false + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=: ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test X-m = "X$prev" && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the '$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=: + if $isdir; then + destdir=$dest + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir=$func_dirname_result + destname=$func_basename_result + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "'$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "'$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir=$func_dirname_result + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking '$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname=$1 + shift + + srcname=$realname + test -n "$relink_command" && srcname=${realname}T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme=$stripme + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme= + ;; + esac + ;; + os2*) + case $realname in + *_dll.a) + tstripme= + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try 'ln -sf' first, because the 'ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib=$destdir/$realname + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name=$func_basename_result + instname=$dir/${name}i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest=$destfile + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to '$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test yes = "$build_old_libs"; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext= + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=.exe + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script '$wrapper'" + + finalize=: + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "'$lib' has not been installed in '$libdir'" + finalize=false + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test no = "$fast_install" && test -n "$relink_command"; then + $opt_dry_run || { + if $finalize; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file=$func_basename_result + outputname=$tmpdir/$file + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_quiet || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink '$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file=$outputname + else + func_warning "cannot relink '$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name=$func_basename_result + + # Set up the ranlib parameters. + oldlib=$destdir/$name + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run '$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test install = "$opt_mode" && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $debug_cmd + + my_outputname=$1 + my_originator=$2 + my_pic_p=${3-false} + my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms=${my_outputname}S.c + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist=$output_objdir/$my_outputname.nm + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* External symbol declarations for the compiler. */\ +" + + if test yes = "$dlself"; then + func_verbose "generating symbol list for '$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from '$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols=$output_objdir/$outputname.exp + $opt_dry_run || { + $RM $export_symbols + eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from '$dlprefile'" + func_basename "$dlprefile" + name=$func_basename_result + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename= + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname"; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename=$func_basename_result + else + # no lafile. user explicitly requested -dlpreopen <import library>. + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename"; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 </dev/null >/dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + func_show_eval '$RM "${nlist}I"' + if test -n "$global_symbol_to_import"; then + eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[];\ +" + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ +static void lt_syminit(void) +{ + LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; + for (; symbol->name; ++symbol) + {" + $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" + echo >> "$output_objdir/$my_dlsyms" "\ + } +}" + fi + echo >> "$output_objdir/$my_dlsyms" "\ +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{ {\"$my_originator\", (void *) 0}," + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ + {\"@INIT@\", (void *) <_syminit}," + fi + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + $my_pic_p && pic_flag_for_symtable=" $pic_flag" + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' + + # Transform the symbol file into the correct name. + symfileobj=$output_objdir/${my_outputname}S.$objext + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for '$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $debug_cmd + + win32_libid_type=unknown + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + case $nm_interface in + "MS dumpbin") + if func_cygming_ms_implib_p "$1" || + func_cygming_gnu_implib_p "$1" + then + win32_nmres=import + else + win32_nmres= + fi + ;; + *) + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s|.*|import| + p + q + } + }'` + ;; + esac + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $debug_cmd + + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $debug_cmd + + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive that possess that section. Heuristic: eliminate + # all those that have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $debug_cmd + + if func_cygming_gnu_implib_p "$1"; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1"; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result= + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $debug_cmd + + f_ex_an_ar_dir=$1; shift + f_ex_an_ar_oldlib=$1 + if test yes = "$lock_old_archive_extraction"; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test yes = "$lock_old_archive_extraction"; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $debug_cmd + + my_gentop=$1; shift + my_oldlibs=${1+"$@"} + my_oldobjs= + my_xlib= + my_xabs= + my_xdir= + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib=$func_basename_result + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir=$my_gentop/$my_xlib_u + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + func_basename "$darwin_archive" + darwin_base_archive=$func_basename_result + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches; do + func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" + $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" + cd "unfat-$$/$darwin_base_archive-$darwin_arch" + func_extract_an_archive "`pwd`" "$darwin_base_archive" + cd "$darwin_curdir" + $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result=$my_oldobjs +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory where it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test yes = "$fast_install"; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + \$ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat <<EOF + +/* $cwrappersource - temporary wrapper executable for $objdir/$outputname + Generated by $PROGRAM (GNU $PACKAGE) $VERSION + + The $output program cannot be directly executed until all the libtool + libraries that it depends on are installed. + + This wrapper executable should never be moved out of the build directory. + If it is, it will not operate correctly. +*/ +EOF + cat <<"EOF" +#ifdef _MSC_VER +# define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#include <stdio.h> +#include <stdlib.h> +#ifdef _MSC_VER +# include <direct.h> +# include <process.h> +# include <io.h> +#else +# include <unistd.h> +# include <stdint.h> +# ifdef __CYGWIN__ +# include <io.h> +# endif +#endif +#include <malloc.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* declarations of non-ANSI functions */ +#if defined __MINGW32__ +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined __CYGWIN__ +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined other_platform || defined ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined _MSC_VER +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +#elif defined __MINGW32__ +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined __CYGWIN__ +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined other platforms ... */ +#endif + +#if defined PATH_MAX +# define LT_PATHMAX PATH_MAX +#elif defined MAXPATHLEN +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ + defined __OS2__ +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free (stale); stale = 0; } \ +} while (0) + +#if defined LT_DEBUGWRAPPER +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <<EOF +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +# define externally_visible volatile +#else +# define externally_visible __attribute__((externally_visible)) volatile +#endif +externally_visible const char * MAGIC_EXE = "$magic_exe"; +const char * LIB_PATH_VARNAME = "$shlibpath_var"; +EOF + + if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + func_to_host_path "$temp_rpath" + cat <<EOF +const char * LIB_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * LIB_PATH_VALUE = ""; +EOF + fi + + if test -n "$dllsearchpath"; then + func_to_host_path "$dllsearchpath:" + cat <<EOF +const char * EXE_PATH_VARNAME = "PATH"; +const char * EXE_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * EXE_PATH_VARNAME = ""; +const char * EXE_PATH_VALUE = ""; +EOF + fi + + if test yes = "$fast_install"; then + cat <<EOF +const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */ +EOF + else + cat <<EOF +const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */ +EOF + fi + + + cat <<"EOF" + +#define LTWRAPPER_OPTION_PREFIX "--lt-" + +static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX; +static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script"; +static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug"; + +int +main (int argc, char *argv[]) +{ + char **newargz; + int newargc; + char *tmp_pathspec; + char *actual_cwrapper_path; + char *actual_cwrapper_name; + char *target_name; + char *lt_argv_zero; + int rval = 127; + + int i; + + program_name = (char *) xstrdup (base_name (argv[0])); + newargz = XMALLOC (char *, (size_t) argc + 1); + + /* very simple arg parsing; don't want to rely on getopt + * also, copy all non cwrapper options to newargz, except + * argz[0], which is handled differently + */ + newargc=0; + for (i = 1; i < argc; i++) + { + if (STREQ (argv[i], dumpscript_opt)) + { +EOF + case $host in + *mingw* | *cygwin* ) + # make stdout use "unix" line endings + echo " setmode(1,_O_BINARY);" + ;; + esac + + cat <<"EOF" + lt_dump_script (stdout); + return 0; + } + if (STREQ (argv[i], debug_opt)) + { + lt_debug = 1; + continue; + } + if (STREQ (argv[i], ltwrapper_option_prefix)) + { + /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX + namespace, but it is not one of the ones we know about and + have already dealt with, above (inluding dump-script), then + report an error. Otherwise, targets might begin to believe + they are allowed to use options in the LTWRAPPER_OPTION_PREFIX + namespace. The first time any user complains about this, we'll + need to make LTWRAPPER_OPTION_PREFIX a configure-time option + or a configure.ac-settable value. + */ + lt_fatal (__FILE__, __LINE__, + "unrecognized %s option: '%s'", + ltwrapper_option_prefix, argv[i]); + } + /* otherwise ... */ + newargz[++newargc] = xstrdup (argv[i]); + } + newargz[++newargc] = NULL; + +EOF + cat <<EOF + /* The GNU banner must be the first non-error debug message */ + lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n"); +EOF + cat <<"EOF" + lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]); + lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name); + + tmp_pathspec = find_executable (argv[0]); + if (tmp_pathspec == NULL) + lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (before symlink chase) at: %s\n", + tmp_pathspec); + + actual_cwrapper_path = chase_symlinks (tmp_pathspec); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (after symlink chase) at: %s\n", + actual_cwrapper_path); + XFREE (tmp_pathspec); + + actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path)); + strendzap (actual_cwrapper_path, actual_cwrapper_name); + + /* wrapper name transforms */ + strendzap (actual_cwrapper_name, ".exe"); + tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1); + XFREE (actual_cwrapper_name); + actual_cwrapper_name = tmp_pathspec; + tmp_pathspec = 0; + + /* target_name transforms -- use actual target program name; might have lt- prefix */ + target_name = xstrdup (base_name (TARGET_PROGRAM_NAME)); + strendzap (target_name, ".exe"); + tmp_pathspec = lt_extend_str (target_name, ".exe", 1); + XFREE (target_name); + target_name = tmp_pathspec; + tmp_pathspec = 0; + + lt_debugprintf (__FILE__, __LINE__, + "(main) libtool target name: %s\n", + target_name); +EOF + + cat <<EOF + newargz[0] = + XMALLOC (char, (strlen (actual_cwrapper_path) + + strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1)); + strcpy (newargz[0], actual_cwrapper_path); + strcat (newargz[0], "$objdir"); + strcat (newargz[0], "/"); +EOF + + cat <<"EOF" + /* stop here, and copy so we don't have to do this twice */ + tmp_pathspec = xstrdup (newargz[0]); + + /* do NOT want the lt- prefix here, so use actual_cwrapper_name */ + strcat (newargz[0], actual_cwrapper_name); + + /* DO want the lt- prefix here if it exists, so use target_name */ + lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1); + XFREE (tmp_pathspec); + tmp_pathspec = NULL; +EOF + + case $host_os in + mingw*) + cat <<"EOF" + { + char* p; + while ((p = strchr (newargz[0], '\\')) != NULL) + { + *p = '/'; + } + while ((p = strchr (lt_argv_zero, '\\')) != NULL) + { + *p = '/'; + } + } +EOF + ;; + esac + + cat <<"EOF" + XFREE (target_name); + XFREE (actual_cwrapper_path); + XFREE (actual_cwrapper_name); + + lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */ + lt_setenv ("DUALCASE", "1"); /* for MSK sh */ + /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must + be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath) + because on Windows, both *_VARNAMEs are PATH but uninstalled + libraries must come first. */ + lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE); + lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE); + + lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n", + nonnull (lt_argv_zero)); + for (i = 0; i < newargc; i++) + { + lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n", + i, nonnull (newargz[i])); + } + +EOF + + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + newargz = prepare_spawn (newargz); + rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + lt_debugprintf (__FILE__, __LINE__, + "(main) failed to launch target \"%s\": %s\n", + lt_argv_zero, nonnull (strerror (errno))); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac + + cat <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal (__FILE__, __LINE__, "memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined HAVE_DOS_BASED_FILE_SYSTEM + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable (const char *path) +{ + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + size_t tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined HAVE_DOS_BASED_FILE_SYSTEM + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined HAVE_DOS_BASED_FILE_SYSTEM + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = (size_t) (q - p); + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (STREQ (str, pat)) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + size_t len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + size_t orig_value_len = strlen (orig_value); + size_t add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + size_t len = strlen (new_value); + while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[--len] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $debug_cmd + + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_suncc_cstd_abi +# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! +# Several compiler flags select an ABI that is incompatible with the +# Cstd library. Avoid specifying it if any are in CXXFLAGS. +func_suncc_cstd_abi () +{ + $debug_cmd + + case " $compile_command " in + *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) + suncc_use_cstd_abi=no + ;; + *) + suncc_use_cstd_abi=yes + ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $debug_cmd + + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # what system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll that has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + os2dllname= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=false + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module=$wl-single_module + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test yes != "$build_libtool_libs" \ + && func_fatal_configuration "cannot build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg=$1 + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir=$arg + prev= + continue + ;; + dlfiles|dlprefiles) + $preload || { + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=: + } + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test no = "$dlself"; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test dlprefiles = "$prev"; then + dlself=yes + elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test dlfiles = "$prev"; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols=$arg + test -f "$arg" \ + || func_fatal_error "symbol file '$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex=$arg + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir=$arg + prev= + continue + ;; + mllvm) + # Clang does not use LLVM to link, so we can simply discard any + # '-mllvm $arg' options when doing the link step. + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + if test none != "$pic_object"; then + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + fi + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file '$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + os2dllname) + os2dllname=$arg + prev= + continue + ;; + precious_regex) + precious_files_regex=$arg + prev= + continue + ;; + release) + release=-$arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test rpath = "$prev"; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds=$arg + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg=$arg + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "'-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test X-export-symbols = "X$arg"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between '-L' and '$1'" + else + func_fatal_error "need path for '-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of '$dir'" + dir=$absdir + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test X-lc = "X$arg" || test X-lm = "X$arg"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test X-lc = "X$arg" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc due to us having libc/libc_r. + test X-lc = "X$arg" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test X-lc = "X$arg" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test X-lc = "X$arg" && continue + ;; + esac + elif test X-lc_r = "X$arg"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -mllvm) + prev=mllvm + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module=$wl-multi_module + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "'-no-install' is ignored for $host" + func_warning "assuming '-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -os2dllname) + prev=os2dllname + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # -fstack-protector* stack protector flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files + # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang/GCC memory and address sanitizer + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*|-fsanitize=*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + -Z*) + if test os2 = "`expr $host : '.*\(os2\)'`"; then + # OS/2 uses -Zxxx to specify OS/2-specific options + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case $arg in + -Zlinker | -Zstack) + prev=xcompiler + ;; + esac + continue + else + # Otherwise treat like 'Some other compiler flag' below + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + fi + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + test none = "$pic_object" || { + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + } + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test dlfiles = "$prev"; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test dlprefiles = "$prev"; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the '$prevarg' option requires an argument" + + if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname=$func_basename_result + libobjs_save=$libobjs + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + # Definition is injected by LT_CONFIG during libtool generation. + func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" + + func_dirname "$output" "/" "" + output_objdir=$func_dirname_result$objdir + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test lib = "$linkmode"; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=false + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test lib,link = "$linkmode,$pass"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs=$tmp_deplibs + fi + + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass"; then + libs=$deplibs + deplibs= + fi + if test prog = "$linkmode"; then + case $pass in + dlopen) libs=$dlfiles ;; + dlpreopen) libs=$dlprefiles ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test lib,dlpreopen = "$linkmode,$pass"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs=$dlprefiles + fi + if test dlopen = "$pass"; then + # Collect dlpreopened libraries + save_deplibs=$deplibs + deplibs= + fi + + for deplib in $libs; do + lib= + found=false + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test lib != "$linkmode" && test prog != "$linkmode"; then + func_warning "'-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test lib = "$linkmode"; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib=$searchdir/lib$name$search_ext + if test -f "$lib"; then + if test .la = "$search_ext"; then + found=: + else + found=false + fi + break 2 + fi + done + done + if $found; then + # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll=$l + done + if test "X$ll" = "X$old_library"; then # only static version available + found=false + func_dirname "$lib" "" "." + ladir=$func_dirname_result + lib=$ladir/$old_library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + else + # deplib doesn't seem to be a libtool library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + ;; # -l + *.ltframework) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test conv = "$pass" && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + if test scan = "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "'-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test link = "$pass"; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=false + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=: + fi + ;; + pass_all) + valid_a_lib=: + ;; + esac + if $valid_a_lib; then + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + else + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + fi + ;; + esac + continue + ;; + prog) + if test link != "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + elif test prog = "$linkmode"; then + if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=: + continue + ;; + esac # case $deplib + + $found || test -f "$lib" \ + || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "'$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir=$func_dirname_result + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass" || + { test prog != "$linkmode" && test lib != "$linkmode"; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test conv = "$pass"; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test prog != "$linkmode" && test lib != "$linkmode"; then + func_fatal_error "'$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test yes = "$prefer_static_libs" || + test built,no = "$prefer_static_libs,$installed"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib=$l + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + + # This library was specified with -dlopen. + if test dlopen = "$pass"; then + test -z "$libdir" \ + && func_fatal_error "cannot -dlopen a convenience library: '$lib'" + if test -z "$dlname" || + test yes != "$dlopen_support" || + test no = "$build_libtool_libs" + then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of '$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir=$ladir + fi + ;; + esac + func_basename "$lib" + laname=$func_basename_result + + # Find the relevant object directory and library name. + if test yes = "$installed"; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library '$lib' was moved." + dir=$ladir + absdir=$abs_ladir + libdir=$abs_ladir + else + dir=$lt_sysroot$libdir + absdir=$lt_sysroot$libdir + fi + test yes = "$hardcode_automatic" && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir=$ladir + absdir=$abs_ladir + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir=$ladir/$objdir + absdir=$abs_ladir/$objdir + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test dlpreopen = "$pass"; then + if test -z "$libdir" && test prog = "$linkmode"; then + func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" + fi + case $host in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test lib = "$linkmode"; then + deplibs="$dir/$old_library $deplibs" + elif test prog,link = "$linkmode,$pass"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test prog = "$linkmode" && test link != "$pass"; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=false + if test no != "$link_all_deplibs" || test -z "$library_names" || + test no = "$build_libtool_libs"; then + linkalldeplibs=: + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if $linkalldeplibs; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test prog,link = "$linkmode,$pass"; then + if test -n "$library_names" && + { { test no = "$prefer_static_libs" || + test built,yes = "$prefer_static_libs,$installed"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then + # Make sure the rpath contains only unique directories. + case $temp_rpath: in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if $alldeplibs && + { test pass_all = "$deplibs_check_method" || + { test yes = "$build_libtool_libs" && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test built = "$use_static_libs" && test yes = "$installed"; then + use_static_libs=no + fi + if test -n "$library_names" && + { test no = "$use_static_libs" || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc* | *os2*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test no = "$installed"; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule= + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule=$dlpremoduletest + break + fi + done + if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then + echo + if test prog = "$linkmode"; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test lib = "$linkmode" && + test yes = "$hardcode_into_libs"; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname=$1 + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname=$dlname + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc* | *os2*) + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + esac + eval soname=\"$soname_spec\" + else + soname=$realname + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot=$soname + func_basename "$soroot" + soname=$func_basename_result + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from '$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for '$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test prog = "$linkmode" || test relink != "$opt_mode"; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test no = "$hardcode_direct"; then + add=$dir/$linklib + case $host in + *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; + *-*-sysv4*uw2*) add_dir=-L$dir ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir=-L$dir ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we cannot + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library"; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add=$dir/$old_library + fi + elif test -n "$old_library"; then + add=$dir/$old_library + fi + fi + esac + elif test no = "$hardcode_minus_L"; then + case $host in + *-*-sunos*) add_shlibpath=$dir ;; + esac + add_dir=-L$dir + add=-l$name + elif test no = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + relink) + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$dir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$absdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test yes != "$lib_linked"; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test prog = "$linkmode"; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test yes != "$hardcode_direct" && + test yes != "$hardcode_minus_L" && + test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test prog = "$linkmode" || test relink = "$opt_mode"; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$libdir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$libdir + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add=-l$name + elif test yes = "$hardcode_automatic"; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib"; then + add=$inst_prefix_dir$libdir/$linklib + else + add=$libdir/$linklib + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir=-L$libdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + fi + + if test prog = "$linkmode"; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test prog = "$linkmode"; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test unsupported != "$hardcode_direct"; then + test -n "$old_library" && linklib=$old_library + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test yes = "$build_libtool_libs"; then + # Not a shared library + if test pass_all != "$deplibs_check_method"; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system cannot link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test yes = "$module"; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test lib = "$linkmode"; then + if test -n "$dependency_libs" && + { test yes != "$hardcode_into_libs" || + test yes = "$build_old_libs" || + test yes = "$link_static"; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs=$temp_deplibs + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test no != "$link_all_deplibs"; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path=$deplib ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of '$dir'" + absdir=$dir + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names"; then + for tmp in $deplibrary_names; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl"; then + depdepl=$absdir/$objdir/$depdepl + darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" + func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" + path= + fi + fi + ;; + *) + path=-L$absdir/$objdir + ;; + esac + else + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "'$deplib' seems to be moved" + + path=-L$absdir + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test link = "$pass"; then + if test prog = "$linkmode"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs=$newdependency_libs + if test dlpreopen = "$pass"; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test dlopen != "$pass"; then + test conv = "$pass" || { + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + } + + if test prog,link = "$linkmode,$pass"; then + vars="compile_deplibs finalize_deplibs" + else + vars=deplibs + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + + # Add Sun CC postdeps if required: + test CXX = "$tagname" && { + case $host_os in + linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C++ 5.9 + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + + solaris*) + func_cc_basename "$CC" + case $func_cc_basename_result in + CC* | sunCC*) + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + esac + } + + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i= + ;; + esac + if test -n "$i"; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test prog = "$linkmode"; then + dlfiles=$newdlfiles + fi + if test prog = "$linkmode" || test lib = "$linkmode"; then + dlprefiles=$newdlprefiles + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "'-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "'-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs=$output + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form 'libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test no = "$module" \ + && func_fatal_help "libtool library '$output' must begin with 'lib'" + + if test no != "$need_lib_prefix"; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test pass_all != "$deplibs_check_method"; then + func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test no = "$dlself" \ + || func_warning "'-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test 1 -lt "$#" \ + && func_warning "ignoring multiple '-rpath's for a libtool library" + + install_libdir=$1 + + oldlibs= + if test -z "$rpath"; then + if test yes = "$build_libtool_libs"; then + # Building a libtool convenience library. + # Some compilers have problems with a '.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "'-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs=$IFS; IFS=: + set dummy $vinfo 0 0 0 + shift + IFS=$save_ifs + + test -n "$7" && \ + func_fatal_help "too many parameters to '-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major=$1 + number_minor=$2 + number_revision=$3 + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # that has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|freebsd-elf|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_revision + ;; + freebsd-aout|qnx|sunos) + current=$number_major + revision=$number_minor + age=0 + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_minor + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type '$version_type'" + ;; + esac + ;; + no) + current=$1 + revision=$2 + age=$3 + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT '$current' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION '$revision' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE '$age' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE '$age' is greater than the current interface number '$current'" + func_fatal_error "'$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + # On Darwin other compilers + case $CC in + nagfor*) + verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + ;; + *) + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + esac + ;; + + freebsd-aout) + major=.$current + versuffix=.$current.$revision + ;; + + freebsd-elf) + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + irix | nonstopux) + if test no = "$lt_irix_increment"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring=$verstring_prefix$major.$revision + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test 0 -ne "$loop"; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring_prefix$major.$iface:$verstring + done + + # Before this point, $major must not contain '.'. + major=.$major + versuffix=$major.$revision + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=.$current.$age.$revision + verstring=$current.$age.$revision + + # Add in all the interfaces that we are compatible with. + loop=$age + while test 0 -ne "$loop"; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring:$iface.0 + done + + # Make executables depend on our current version. + func_append verstring ":$current.0" + ;; + + qnx) + major=.$current + versuffix=.$current + ;; + + sco) + major=.$current + versuffix=.$current + ;; + + sunos) + major=.$current + versuffix=.$current.$revision + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 file systems. + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + + *) + func_fatal_configuration "unknown library version type '$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring=0.0 + ;; + esac + if test no = "$need_version"; then + versuffix= + else + versuffix=.0.0 + fi + fi + + # Remove version info from name if versioning should be avoided + if test yes,no = "$avoid_version,$need_version"; then + major= + versuffix= + verstring= + fi + + # Check to see if the archive will have undefined symbols. + if test yes = "$allow_undefined"; then + if test unsupported = "$allow_undefined_flag"; then + if test yes = "$build_old_libs"; then + func_warning "undefined symbols not allowed in $host shared libraries; building static only" + build_libtool_libs=no + else + func_fatal_error "can't build $host shared library unless -no-undefined is specified" + fi + fi + else + # Don't allow undefined symbols. + allow_undefined_flag=$no_undefined_flag + fi + + fi + + func_generate_dlsyms "$libname" "$libname" : + func_append libobjs " $symfileobj" + test " " = "$libobjs" && libobjs= + + if test relink != "$opt_mode"; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) + if test -n "$precious_files_regex"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles=$dlfiles + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles=$dlprefiles + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test yes = "$build_libtool_libs"; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test yes = "$build_libtool_need_lc"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release= + versuffix= + major= + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c <<EOF + int main() { return 0; } +EOF + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then + ldd_output=`ldd conftest` + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $i "*) + func_append newdeplibs " $i" + i= + ;; + esac + fi + if test -n "$i"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then + func_append newdeplibs " $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which I believe you do not have" + echo "*** because a test_compile did reveal that the linker did not use it for" + echo "*** its dynamic dependency list that programs get resolved with at runtime." + fi + fi + ;; + *) + func_append newdeplibs " $i" + ;; + esac + done + else + # Error occurred in the first compile. Let's try to salvage + # the situation: Compile a separate program for each library. + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $i; then + ldd_output=`ldd conftest` + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $i "*) + func_append newdeplibs " $i" + i= + ;; + esac + fi + if test -n "$i"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then + func_append newdeplibs " $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because a test_compile did reveal that the linker did not use this one" + echo "*** as a dynamic dependency that programs can get resolved with at runtime." + fi + fi + else + droppeddeps=yes + echo + $ECHO "*** Warning! Library $i is needed by this library but I was not able to" + echo "*** make it link in! You will probably need to install it or some" + echo "*** library that it depends on before this library will be fully" + echo "*** functional. Installing it before continuing would be even better." + fi + ;; + *) + func_append newdeplibs " $i" + ;; + esac + done + fi + ;; + file_magic*) + set dummy $deplibs_check_method; shift + file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib= + ;; + esac + fi + if test -n "$a_deplib"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + if test -n "$file_magic_glob"; then + libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob` + else + libnameglob=$libname + fi + test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + if test yes = "$want_nocaseglob"; then + shopt -s nocaseglob + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib=$potent_lib + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | $SED 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; + *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib= + ;; + esac + fi + if test -n "$a_deplib"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib=$potent_lib # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs= + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + for i in $predeps $postdeps; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test none = "$deplibs_check_method"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test yes = "$droppeddeps"; then + if test yes = "$module"; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test no = "$allow_undefined"; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs=$new_libs + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test yes = "$build_libtool_libs"; then + # Remove $wl instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test yes = "$hardcode_into_libs"; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath=$finalize_rpath + test relink = "$opt_mode" || rpath=$compile_rpath$rpath + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath=$finalize_shlibpath + test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname=$1 + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname=$realname + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib=$output_objdir/$realname + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols=$output_objdir/$libname.uexp + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + func_dll_def_p "$export_symbols" || { + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols=$export_symbols + export_symbols= + always_export_symbols=yes + } + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs=$IFS; IFS='~' + for cmd1 in $cmds; do + IFS=$save_ifs + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test yes = "$try_normal_branch" \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=$output_objdir/$output_la.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS=$save_ifs + if test -n "$export_symbols_regex" && test : != "$skipped_export"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test : != "$skipped_export" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs=$tmp_deplibs + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test yes = "$compiler_needs_object" && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test : != "$skipped_export" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then + output=$output_objdir/$output_la.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then + output=$output_objdir/$output_la.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test yes = "$compiler_needs_object"; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-$k.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test -z "$objlist" || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test 1 -eq "$k"; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-$k.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-$k.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + ${skipped_export-false} && { + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + } + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs=$IFS; IFS='~' + for cmd in $concat_cmds; do + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + ${skipped_export-false} && { + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + } + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs=$IFS; IFS='~' + for cmd in $cmds; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test yes = "$module" || test yes = "$export_dynamic"; then + # On all known operating systems, these are identical. + dlname=$soname + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "'-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object '$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj=$output + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # if reload_cmds runs $LD directly, get rid of -Wl from + # whole_archive_flag_spec and hope we can get by with turning comma + # into space. + case $reload_cmds in + *\$LD[\ \$]*) wl= ;; + esac + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags + else + gentop=$output_objdir/${obj}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test yes = "$build_libtool_libs" || libobjs=$non_pic_objects + + # Create the old-style object. + reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs + + output=$obj + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + test yes = "$build_libtool_libs" || { + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + } + + if test -n "$pic_flag" || test default != "$pic_mode"; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output=$libobj + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "'-release' is ignored for programs" + + $preload \ + && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ + && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test CXX = "$tagname"; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " $wl-bind_at_load" + func_append finalize_command " $wl-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs=$new_libs + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath=$rpath + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath=$rpath + + if test -n "$libobjs" && test yes = "$build_old_libs"; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" false + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=: + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=false + ;; + *cygwin* | *mingw* ) + test yes = "$build_libtool_libs" || wrappers_required=false + ;; + *) + if test no = "$need_relink" || test yes != "$build_libtool_libs"; then + wrappers_required=false + fi + ;; + esac + $wrappers_required || { + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command=$compile_command$compile_rpath + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.$objext"; then + func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' + fi + + exit $exit_status + } + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test yes = "$no_install"; then + # We don't need to create a wrapper script. + link_command=$compile_var$compile_command$compile_rpath + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + case $hardcode_action,$fast_install in + relink,*) + # Fast installation is not supported + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "'$output' will be relinked during installation" + ;; + *,yes) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + ;; + *,no) + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + ;; + *,needless) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command= + ;; + esac + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource=$output_path/$objdir/lt-$output_name.c + cwrapper=$output_path/$output_name.exe + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host"; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + case $build_libtool_libs in + convenience) + oldobjs="$libobjs_save $symfileobj" + addlibs=$convenience + build_libtool_libs=no + ;; + module) + oldobjs=$libobjs_save + addlibs=$old_convenience + build_libtool_libs=no + ;; + *) + oldobjs="$old_deplibs $non_pic_objects" + $preload && test -f "$symfileobj" \ + && func_append oldobjs " $symfileobj" + addlibs=$old_convenience + ;; + esac + + if test -n "$addlibs"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase=$func_basename_result + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj"; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test -z "$oldobjs"; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test yes = "$build_old_libs" && old_library=$libname.$libext + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test yes = "$hardcode_automatic"; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test yes = "$installed"; then + if test -z "$install_libdir"; then + break + fi + output=$output_objdir/${outputname}i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name=$func_basename_result + func_resolve_sysroot "$deplib" + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs=$newdependency_libs + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles=$newdlprefiles + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles=$newdlprefiles + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test -n "$bindir"; then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result/$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test no,yes = "$installed,$need_relink"; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +if test link = "$opt_mode" || test relink = "$opt_mode"; then + func_mode_link ${1+"$@"} +fi + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $debug_cmd + + RM=$nonopt + files= + rmforce=false + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=: ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir=$func_dirname_result + if test . = "$dir"; then + odir=$objdir + else + odir=$dir/$objdir + fi + func_basename "$file" + name=$func_basename_result + test uninstall = "$opt_mode" && odir=$dir + + # Remember odir for removal later, being careful to avoid duplicates + if test clean = "$opt_mode"; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif $rmforce; then + continue + fi + + rmfiles=$file + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case $opt_mode in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && test none != "$pic_object"; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && test none != "$non_pic_object"; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test clean = "$opt_mode"; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.$objext" + if test yes = "$fast_install" && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name"; then + func_append rmfiles " $odir/lt-$noexename.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the $objdir's in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then + func_mode_uninstall ${1+"$@"} +fi + +test -z "$opt_mode" && { + help=$generic_help + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode '$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# where we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/m4/libtool.m4 b/m4/libtool.m4 new file mode 100644 index 0000000..10ab284 --- /dev/null +++ b/m4/libtool.m4 @@ -0,0 +1,8388 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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, see <http://www.gnu.org/licenses/>. +]) + +# serial 58 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + +# _LT_CC_BASENAME(CC) +# ------------------- +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. +m4_defun([_LT_CC_BASENAME], +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from 'configure', and 'config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain=$ac_aux_dir/ltmain.sh +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the 'libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags='_LT_TAGS'dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# '#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test 0 = "$lt_write_fail" && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +'$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to <bug-libtool@gnu.org>." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test 0 != $[#] +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try '$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try '$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test yes = "$silent" && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +_LT_COPYING +_LT_LIBTOOL_TAGS + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS=$save_LDFLAGS + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + m4_if([$1], [CXX], +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case $ECHO in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([$with_sysroot]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and where our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test yes = "[$]$2"; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS +]) + +if test yes = "[$]$2"; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n "$lt_cv_sys_max_cmd_len"; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes = "$cross_compiling"; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen=shl_load], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen=dlopen], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then + + # We can hardcode non-existent directories. + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program that can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac]) +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program that can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test no = "$withval" || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in + *GNU* | *'with BFD'*) + test no != "$with_gnu_ld" && break + ;; + *) + test yes != "$with_gnu_ld" && break + ;; + esac + fi + done + IFS=$lt_save_ifs +else + lt_cv_path_LD=$LD # Let the user override the test with a path. +fi]) +LD=$lt_cv_path_LD +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +_LT_PATH_LD_GNU +AC_SUBST([LD]) + +_LT_TAGDECL([], [LD], [1], [The linker used to build libraries]) +])# LT_PATH_LD + +# Old names: +AU_ALIAS([AM_PROG_LD], [LT_PATH_LD]) +AU_ALIAS([AC_PROG_LD], [LT_PATH_LD]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_LD], []) +dnl AC_DEFUN([AC_PROG_LD], []) + + +# _LT_PATH_LD_GNU +#- -------------- +m4_defun([_LT_PATH_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac]) +with_gnu_ld=$lt_cv_prog_gnu_ld +])# _LT_PATH_LD_GNU + + +# _LT_CMD_RELOAD +# -------------- +# find reload flag for linker +# -- PORTME Some linkers may need a different reload flag. +m4_defun([_LT_CMD_RELOAD], +[AC_CACHE_CHECK([for $LD option to reload object files], + lt_cv_ld_reload_flag, + [lt_cv_ld_reload_flag='-r']) +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test yes != "$GCC"; then + reload_cmds=false + fi + ;; + darwin*) + if test yes = "$GCC"; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac +_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl +_LT_TAGDECL([], [reload_cmds], [2])dnl +])# _LT_CMD_RELOAD + + +# _LT_PATH_DD +# ----------- +# find a working dd +m4_defun([_LT_PATH_DD], +[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + +# _LT_CHECK_MAGIC_METHOD +# ---------------------- +# how to check for library dependencies +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_MAGIC_METHOD], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +AC_CACHE_CHECK([how to recognize dependent libraries], +lt_cv_deplibs_check_method, +[lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[[4-9]]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[[45]]*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi]) +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM=-lm) + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test yes = "$GCC"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + osf3*) + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting $shlibpath_var if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC=$CC +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report what library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC=$lt_save_CC +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)=$prev$p + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)=$p + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)=$p + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test no = "$F77"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_F77"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test no = "$FC"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_FC"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_FC" + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code=$lt_simple_compile_test_code + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f "$lt_ac_sed" && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test 10 -lt "$lt_ac_count" && break + lt_ac_count=`expr $lt_ac_count + 1` + if test "$lt_ac_count" -gt "$lt_ac_max"; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine what file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 new file mode 100644 index 0000000..94b0829 --- /dev/null +++ b/m4/ltoptions.m4 @@ -0,0 +1,437 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 8 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option '$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' +# LT_INIT options. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 new file mode 100644 index 0000000..48bc934 --- /dev/null +++ b/m4/ltsugar.m4 @@ -0,0 +1,124 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59, which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 new file mode 100644 index 0000000..fa04b52 --- /dev/null +++ b/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 4179 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.6' +macro_revision='2.4.6' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 new file mode 100644 index 0000000..c6b26f8 --- /dev/null +++ b/m4/lt~obsolete.m4 @@ -0,0 +1,99 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/objects/Makefile.am b/objects/Makefile.am new file mode 100644 index 0000000..fea2030 --- /dev/null +++ b/objects/Makefile.am @@ -0,0 +1,67 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)\ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/objects"' + +noinst_LIBRARIES = librtfl-objects.a + +bin_PROGRAMS = rtfl-objbase rtfl-objcount rtfl-objview + +rtfl_objbase_SOURCES = rtfl_objbase.cc + +rtfl_objbase_LDADD = \ + librtfl-objects.a \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +librtfl_objects_a_SOURCES = \ + objdelete_controller.hh \ + objdelete_controller.cc \ + objects_buffer.hh \ + objects_buffer.cc \ + objects_parser.hh \ + objects_parser.cc \ + objects_writer.hh \ + objects_writer.cc \ + objident_controller.hh \ + objident_controller.cc + +rtfl_objcount_SOURCES = \ + objcount_controller.hh \ + objcount_controller.cc \ + objcount_window.hh \ + objcount_window.cc \ + rtfl_objcount.cc + +rtfl_objcount_LDADD = \ + librtfl-objects.a \ + ../common/librtfl-common.a \ + ../common/librtfl-tools.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + +rtfl_objview_SOURCES = \ + objview_commands.hh \ + objview_commands.cc \ + objview_graph.hh \ + objview_graph.cc \ + objview_controller.hh \ + objview_controller.cc \ + objview_stacktrace.hh \ + objview_stacktrace.cc \ + objview_window.hh \ + objview_window.cc \ + rtfl_objview.cc + +rtfl_objview_LDADD = \ + librtfl-objects.a \ + ../common/librtfl-common.a \ + ../common/librtfl-tools.a \ + ../dwr/libDw-rtfl.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + +if USE_GRAPH2 +rtfl_objview_LDADD += @GRAPHVIZ_LIBS@ +endif diff --git a/objects/objcount_controller.cc b/objects/objcount_controller.cc new file mode 100644 index 0000000..b7ce4e7 --- /dev/null +++ b/objects/objcount_controller.cc @@ -0,0 +1,114 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objcount_controller.hh" + +using namespace rtfl::tools; + +namespace rtfl { + +namespace objects { + +void ObjCountController::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + table->registerObject (id); +} + +void ObjCountController::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + table->registerObject (id); +} + + void ObjCountController::objMsgStart (CommonLineInfo *info, const char *id) +{ + table->registerObject (id); +} + +void ObjCountController::objMsgEnd (CommonLineInfo *info, const char *id) +{ + table->registerObject (id); +} + +void ObjCountController::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *funname, const char *args) +{ + table->registerObject (id); +} + +void ObjCountController::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + table->registerObject (id); +} + +void ObjCountController::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + table->createObject (id, klass); +} + +void ObjCountController::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + // TODO Is this not done by ObjdentController? + table->addIdentity (id1, id2); +} + +void ObjCountController::objNoIdent (CommonLineInfo *info) +{ +} + +void ObjCountController::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + table->registerObject (parent); + table->registerObject (child); +} + +void ObjCountController::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + table->registerObject (id); +} + +void ObjCountController::objClassColor (CommonLineInfo *info, const char *klass, + const char *color) +{ + table->setClassColor (klass, color); +} + +void ObjCountController::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + table->registerObject (id); +} + +void ObjCountController::objDelete (CommonLineInfo *info, const char *id) +{ + table->deleteObject (id); +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objcount_controller.hh b/objects/objcount_controller.hh new file mode 100644 index 0000000..6af5ba7 --- /dev/null +++ b/objects/objcount_controller.hh @@ -0,0 +1,50 @@ +#ifndef __OBJECTS_OBJCOUNT_CONTROLLER_HH__ +#define __OBJECTS_OBJCOUNT_CONTROLLER_HH__ + +#include "objects_parser.hh" +#include "objcount_window.hh" + +namespace rtfl { + +namespace objects { + +class ObjCountController: public ObjectsControllerBase +{ +private: + ObjCountTable *table; + +public: + ObjCountController (ObjCountTable *table) { this->table = table; } + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJCOUNT_CONTROLLER_HH__ diff --git a/objects/objcount_window.cc b/objects/objcount_window.cc new file mode 100644 index 0000000..d7adf06 --- /dev/null +++ b/objects/objcount_window.cc @@ -0,0 +1,445 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objcount_window.hh" +#include "common/about.hh" + +#include <FL/Fl_Menu_Bar.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/fl_draw.H> + +using namespace lout::container::typed; +using namespace lout::object; +using namespace lout::misc; + +namespace rtfl { + +namespace objects { + +ObjCountTable::Class::Class (const char *name) +{ + this->name = strdup (name); + count = new lout::misc::SimpleVector<int> (1); + count->increase (); + *(count->getLastRef()) = 0; +} + + +ObjCountTable::Class::~Class () +{ + free (name); + delete count; +} + + +int ObjCountTable::Class::compareTo(Comparable *other) +{ + return strcmp (name, ((Class*)other)->name); +} + + +void ObjCountTable::Class::create () +{ + (*(count->getLastRef()))++; +} + + +void ObjCountTable::Class::remove () +{ + (*(count->getLastRef()))--; +} + + +void ObjCountTable::Class::newSnapshot () +{ + int c = *(count->getLastRef()); + count->increase (); + *(count->getLastRef()) = c; +} + + +// ---------------------------------------------------------------------- + + +int ObjCountTable::Object::classSernoGlobal = 0; + + +ObjCountTable::Object::Object (Class *klass) +{ + setClass (klass); + refCount = 0; +} + + +ObjCountTable::Object::~Object () +{ +} + + +void ObjCountTable::Object::setClass (Class *klass) +{ + this->klass = klass; + classSerno = classSernoGlobal++; +} + + +// ---------------------------------------------------------------------- + + +ObjCountTable::ObjectRef::ObjectRef (rtfl::objects::ObjCountTable::Object + *object) +{ + this->object = object; + object->ref (); +} + + +ObjCountTable::ObjectRef::~ObjectRef () +{ + object->unref (); +} + + +// ---------------------------------------------------------------------- + + +ObjCountTable::ObjCountTable (int x, int y, int width, int height, + const char *label) : + Fl_Table (x, y, width, height, label) +{ + rows (0); + row_header (true); + row_height_all (20); + row_resize (false); + + cols (1); + col_header (false); + row_header_width (200); + col_width_all (80); + col_resize (true); + + end(); + + objects = new HashTable<String, ObjectRef> (true, true); + identities = new HashTable<String, String> (true, true); + identitiesRev = new HashTable<String, String> (false, false); + classes = new HashTable<String, Class> (true, false); + classesList = new Vector<Class> (1, true); + + ensureClass ("<unknown>"); +} + + +ObjCountTable::~ObjCountTable() +{ + delete objects; + delete identitiesRev; + delete identities; + delete classes; + delete classesList; +} + + +void ObjCountTable::draw_cell (TableContext context, int row, int col, int x, + int y, int width, int height) +{ + switch (context) { + case CONTEXT_COL_HEADER: + break; + + case CONTEXT_ROW_HEADER: + fl_push_clip (x, y, width, height); + fl_draw_box (FL_THIN_UP_BOX, x, y, width, height, row_header_color ()); + fl_color (FL_BLACK); + fl_draw (classesList->get(row)->name, x, y, width, height, FL_ALIGN_LEFT); + fl_pop_clip (); + break; + + case CONTEXT_CELL: + fl_push_clip (x, y, width, height); + fl_color (FL_WHITE); + fl_rectf (x, y, width, height); + fl_color (FL_BLACK); + char buf[6]; + snprintf (buf, 6, "%d", classesList->get(row)->count->get (col)); + fl_draw (buf, x, y, width, height, FL_ALIGN_RIGHT); + fl_pop_clip (); + break; + + default: + // compiler happyness + break; + } +} + + +ObjCountTable::Class *ObjCountTable::ensureClass (const char *className) +{ + String key (className); + Class *klass = classes->get (&key); + + if (klass == NULL) { + klass = new Class (className); + classes->put (new String (className), klass); + + int i = classesList->bsearch (klass, false); + classesList->insert (klass, i); + for (int j = i; j < classesList->size (); j++) + classesList->get(j)->index = j; + + rows (rows () + 1); + } + + return klass; +} + +void ObjCountTable::createObject (const char *id, const char *className) +{ + Class *klass = ensureClass (className); + + String key (id); + ObjectRef *objectRef = objects->get (&key); + + if (objectRef != NULL) { + objectRef->object->getClass()->remove (); + objectRef->object->setClass (klass); + } else { + rtfl::objects::ObjCountTable::Object *object; + String *id2 = identities->get (&key); + if (id2) { + ObjectRef *objRef2 = objects->get (id2); + assert (objRef2 != NULL); + object = objRef2->object; + } else + object = new rtfl::objects::ObjCountTable::Object (klass); + + objectRef = new ObjectRef (object); + objects->put (new String (id), objectRef); + } + + klass->create (); + damage_zone(klass->index, cols () - 1, klass->index, cols () - 1); +} + + +void ObjCountTable::deleteObject (const char *id) +{ + String key (id); + ObjectRef *objectRef = objects->get (&key); + if (objectRef != NULL) { + objectRef->object->getClass()->remove (); + damage_zone(objectRef->object->getClass()->index,cols () - 1, + objectRef->object->getClass()->index, cols () - 1); + + objects->remove (&key); + + String *key2 = identities->get (&key); + if (key2) { + identitiesRev->remove (key2); + identities->remove (&key); + } else { + key2 = identitiesRev->get (&key); + if (key2) { + identitiesRev->remove (&key); + identities->remove (key2); + } + } + } +} + + +void ObjCountTable::registerObject (const char *id) +{ + String key (id); + if (!objects->contains (&key)) + createObject (id, "<unknown>"); +} + + +void ObjCountTable::addIdentity (const char *id1, const char *id2) +{ + if (strcmp (id1, id2) != 0) { + // Note: An ObjectRef (which is != NULL) points always to an Object. + String key1 (id1), key2 (id2); + ObjectRef *objRef1 = objects->get (&key1), + *objRef2 = objects->get (&key2); + + if (objRef1 == NULL && objRef2 == NULL) { + // Neither defined: create both. + registerObject (id1); + insertIdentity (id2, id1); + registerObject (id2); + } else if (objRef1 == NULL) { + // First not defined, but second: create from second. + insertIdentity (id2, id1); + registerObject (id2); + } else if (objRef2 == NULL) { + // Vice versa. + insertIdentity (id1, id2); + registerObject (id1); + } else { + // Both already defined ... + if (objRef1->object == objRef2->object) + // ... for same object: caller's fault. + fprintf (stderr, "WARNING: Identity of '%s' and '%s' added twice.", + id1, id2); + else { + // ... for different objects. + if (objRef1->object->getClassSerno () > + objRef2->object->getClassSerno ()) { + // Class definition of first object more recent, so assign it to + // second. + objRef2->object->getClass()->remove (); + damage_zone(objRef2->object->getClass()->index,cols () - 1, + objRef2->object->getClass()->index, cols () - 1); + objRef2->object->unref (); + objRef2->object = objRef1->object; + objRef2->object->ref (); + } else { + // Vice versa. + objRef1->object->getClass()->remove (); + damage_zone(objRef1->object->getClass()->index,cols () - 1, + objRef1->object->getClass()->index, cols () - 1); + objRef1->object->unref (); + objRef1->object = objRef2->object; + objRef1->object->ref (); + } + } + } + } +} + + +void ObjCountTable::insertIdentity (const char *id1, const char *id2) +{ + String *s1 = new String (id1), *s2 = new String (id2); + identities->put (s1, s2); + identitiesRev->put (s2, s1); +} + + +void ObjCountTable::setClassColor (const char *klass, const char *color) +{ +} + + +void ObjCountTable::newSnapshot () +{ + for (int i = 0; i < classesList->size (); i++) + classesList->get(i)->newSnapshot (); + + cols (cols () + 1); +} + + +void ObjCountTable::removeOldestSnapshot () +{ +} + + +// ---------------------------------------------------------------------- + + +ObjCountWindow::ObjCountWindow (int width, int height, const char *title) : + Fl_Window (width, height, title) +{ + int menuHeight = 24; + + callback(windowCallback, NULL); + box(FL_NO_BOX); + + Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, width, menuHeight); + + table = new ObjCountTable (0, menuHeight, width, height - menuHeight); + + Fl_Menu_Item menuItems[] = { + { "&File", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "Quit", FL_COMMAND + 'q', quit, this, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Snapshot", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "New &snapshot", FL_COMMAND + 's', newSnapshot, this, 0, 0, 0, 0, 0 }, + { "&Delete oldest", FL_COMMAND + 'd', removeOldestSnapshot, this, + 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Help", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "&About RTFL", 0, about, this, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + menu->copy(menuItems); + + resizable(table); + + aboutWindow = NULL; +} + + +ObjCountWindow::~ObjCountWindow () +{ + if (aboutWindow) + delete aboutWindow; +} + + +void ObjCountWindow::windowCallback (Fl_Widget *widget, void *data) +{ + // Ignore escape key. TODO Looks rather hackish to me. + if (Fl::event_key() != FL_Escape) + quit (widget, data); +} + + +void ObjCountWindow::quit (Fl_Widget *widget, void *data) +{ + exit (0); +} + + +void ObjCountWindow::newSnapshot (Fl_Widget *widget, void *data) +{ + ObjCountWindow *window = (ObjCountWindow*)data; + window->table->newSnapshot (); +} + + +void ObjCountWindow::removeOldestSnapshot (Fl_Widget *widget, void *data) +{ + ObjCountWindow *window = (ObjCountWindow*)data; + window->table->removeOldestSnapshot (); +} + + +void ObjCountWindow::about (Fl_Widget *widget, void *data) +{ + ObjCountWindow *window = (ObjCountWindow*)data; + + if (window->aboutWindow == NULL) + window->aboutWindow = + new common::AboutWindow("rtfl-objcount", "", + common::AboutWindow::HEIGHT_SIMPLE); + window->aboutWindow->show (); +} + + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objcount_window.hh b/objects/objcount_window.hh new file mode 100644 index 0000000..485be55 --- /dev/null +++ b/objects/objcount_window.hh @@ -0,0 +1,118 @@ +#ifndef __OBJECTS_OBJCOUNT_WINDOW_HH__ +#define __OBJECTS_OBJCOUNT_WINDOW_HH__ + +#include <FL/Fl_Window.H> +#include <FL/Fl_Table.H> + +#include "lout/object.hh" +#include "lout/container.hh" +#include "lout/misc.hh" + +namespace rtfl { + +namespace objects { + +class ObjCountTable : public Fl_Table +{ +private: + class Class: public lout::object::Comparable + { + public: + char *name; + int index; + lout::misc::SimpleVector<int> *count; + + Class (const char *name); + ~Class (); + + int compareTo(Comparable *other); + + void create (); + void remove (); + void newSnapshot (); + }; + + class Object: public lout::object::Object + { + private: + static int classSernoGlobal; + + Class *klass; + int classSerno; + int refCount; + + ~Object (); + + public: + Object (Class *klass); + + inline void ref () { refCount++; } + inline void unref () { if (--refCount == 0) delete this; } + + inline Class *getClass () { return klass; } + void setClass (Class *klass); + inline int getClassSerno () { return classSerno; } + }; + + class ObjectRef: public lout::object::Object + { + public: + rtfl::objects::ObjCountTable::Object *object; + + ObjectRef (rtfl::objects::ObjCountTable::Object *object); + ~ObjectRef (); + }; + + lout::container::typed::HashTable<lout::object::String, ObjectRef> *objects; + lout::container::typed::HashTable<lout::object::String, lout::object::String> + *identities, *identitiesRev; + lout::container::typed::HashTable<lout::object::String, Class> *classes; + lout::container::typed::Vector<Class> *classesList; + + Class *ensureClass (const char *className); + void insertIdentity (const char *id1, const char *id2); + +public: + ObjCountTable (int x, int y, int width, int height, + const char *label = NULL); + ~ObjCountTable(); + + void draw_cell (TableContext context, int row, int col, int x, int y, + int width, int height); + + void createObject (const char *id, const char *className); + void deleteObject (const char *id); + void registerObject (const char *id); + void addIdentity (const char *id1, const char *id2); + void setClassColor (const char *klass, const char *color); + void newSnapshot (); + void removeOldestSnapshot (); +}; + + +class ObjCountWindow: public Fl_Window +{ +private: + Fl_Window *aboutWindow; + ObjCountTable *table; + + static void windowCallback (Fl_Widget *widget, void *data); + static void quit (Fl_Widget *widget, void *data); + static void newSnapshot (Fl_Widget *widget, void *data); + static void removeOldestSnapshot (Fl_Widget *widget, void *data); + static void about (Fl_Widget *widget, void *data); + +public: + ObjCountWindow (int width, int height, const char *title); + ~ObjCountWindow (); + + inline ObjCountTable *getTable () { return table; } +}; + + + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJCOUNT_WINDOW_HH__ diff --git a/objects/objdelete_controller.cc b/objects/objdelete_controller.cc new file mode 100644 index 0000000..8224029 --- /dev/null +++ b/objects/objdelete_controller.cc @@ -0,0 +1,204 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objdelete_controller.hh" + +using namespace lout::object; +using namespace lout::misc; +using namespace lout::container::typed; +using namespace rtfl::tools; + +namespace rtfl { + +namespace objects { + +ObjDeleteController::ObjInfo::ObjInfo (const char *id) +{ + numCreated = 0; + numDeleted = 0; + origId = strdup (id); + mappedId = strdup (id); +} + +ObjDeleteController::ObjInfo::~ObjInfo () +{ + free (origId); + free (mappedId); +} + +void ObjDeleteController::ObjInfo::use () +{ + numCreated = max (numCreated, 1); +} + +void ObjDeleteController::ObjInfo::objCreate () +{ + numCreated++; +} + +void ObjDeleteController::ObjInfo::objDelete () +{ + numCreated--; + if (numCreated <= 0) { + numCreated = 0; + numDeleted++; + + free (mappedId); + mappedId = (char*) malloc ((strlen (origId) + 10 + 1) * sizeof (char)); + sprintf (mappedId, "%s-%d", origId, numDeleted); + } +} + +ObjDeleteController::ObjDeleteController (ObjectsController *successor) +{ + this->successor = successor; + successor->setObjectsSource (this); + setObjectsSink (successor); + + objInfos = new HashTable<String, ObjInfo> (true, true); +} + +ObjDeleteController::~ObjDeleteController () +{ + delete objInfos; +} + +void ObjDeleteController::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + successor->objMsg (info, mapId (id), aspect, prio, message); +} + +void ObjDeleteController::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + successor->objMark (info, mapId (id), aspect, prio, message); +} + +void ObjDeleteController::objMsgStart (CommonLineInfo *info, const char *id) +{ + successor->objMsgStart (info, mapId (id)); +} + +void ObjDeleteController::objMsgEnd (CommonLineInfo *info, const char *id) +{ + successor->objMsgEnd (info, mapId (id)); +} + +void ObjDeleteController::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *funname, const char *args) +{ + successor->objEnter (info, mapId (id), aspect, prio, funname, args); +} + +void ObjDeleteController::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + successor->objLeave (info, mapId (id), vals); +} + +void ObjDeleteController::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + ensureObjInfo(id)->objCreate (); + successor->objCreate (info, mapId (id), klass); +} + +void ObjDeleteController::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + successor->objIdent (info, mapId (id1), mapId (id2)); +} + +void ObjDeleteController::objNoIdent (CommonLineInfo *info) +{ + successor->objNoIdent (info); +} + +void ObjDeleteController::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + successor->objAssoc (info, mapId (parent), mapId (child)); +} + +void ObjDeleteController::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + successor->objSet (info, mapId (id), var, val); +} + +void ObjDeleteController::objClassColor (CommonLineInfo *info, + const char *klass, const char *color) +{ + successor->objClassColor (info, klass, color); +} + +void ObjDeleteController::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + successor->objObjectColor (info, mapId (id), color); +} + +void ObjDeleteController::objDelete (CommonLineInfo *info, const char *id) +{ + successor->objDelete (info, mapId (id)); + ensureObjInfo(id)->objDelete (); +} + +const char *ObjDeleteController::mapId (const char *id) +{ + ObjInfo *objInfo = ensureObjInfo (id); + objInfo->use (); + return objInfo->getMappedId (); +} + +ObjDeleteController::ObjInfo *ObjDeleteController::getObjInfo (const char *id) +{ + String key (id); + return objInfos->get (&key); +} + +ObjDeleteController::ObjInfo *ObjDeleteController::ensureObjInfo (const char + *id) +{ + ObjInfo *objInfo = getObjInfo (id); + if (objInfo == NULL) { + objInfo = new ObjInfo (id); + objInfos->put (new String (id), objInfo); + } + return objInfo; +} + +} // namespace objects + +} // namespace rtfl + diff --git a/objects/objdelete_controller.hh b/objects/objdelete_controller.hh new file mode 100644 index 0000000..eccee06 --- /dev/null +++ b/objects/objdelete_controller.hh @@ -0,0 +1,78 @@ +#ifndef __OBJECTS_OBJDELETE_CONTROLLER_HH__ +#define __OBJECTS_OBJDELETE_CONTROLLER_HH__ + +#include "objects_parser.hh" +#include "common/tools.hh" + +namespace rtfl { + +namespace objects { + +/** + * \brief Processes `obj-delete` specially and maps ids of deleted objects to + * new ones, if they are reused. + */ +class ObjDeleteController: public ObjectsControllerBase +{ +private: + class ObjInfo: public lout::object::Object + { + private: + int numCreated, numDeleted; + char *origId, *mappedId; + + public: + ObjInfo (const char *id); + ~ObjInfo (); + + void use (); + void objCreate (); + void objDelete (); + + inline const char *getMappedId () { return mappedId; } + }; + + ObjectsController *successor; + lout::container::typed::HashTable<lout::object::String, ObjInfo> *objInfos; + + const char *mapId (const char *id); + bool objInfoCreated (const char *id); + ObjInfo *getObjInfo (const char *id); + ObjInfo *ensureObjInfo (const char *id); + +public: + ObjDeleteController (ObjectsController *successor); + ~ObjDeleteController (); + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJDELETE_CONTROLLER_HH__ diff --git a/objects/objects_buffer.cc b/objects/objects_buffer.cc new file mode 100644 index 0000000..e669142 --- /dev/null +++ b/objects/objects_buffer.cc @@ -0,0 +1,281 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objects_buffer.hh" + +using namespace lout::object; +using namespace lout::container::typed; +using namespace rtfl::tools; + +namespace rtfl { + +namespace objects { + +ObjectsBuffer::ObjectCommand::ObjectCommand (CommandType type, + CommonLineInfo *info, + const char *fmt, ...) +{ + this->type = type; + this->info.fileName = strdup (info->fileName); + this->info.lineNo = info->lineNo; + this->info.processId = info->processId; + this->info.completeLine = strdup (info->completeLine); + + numArgs = strlen (fmt); + args = new Arg[numArgs]; + + va_list vargs; + va_start (vargs, fmt); + + char *s; + for (int i = 0; fmt[i]; i++) { + args[i].type = fmt[i]; + switch (fmt[i]) { + case 'd': + args[i].d = va_arg(vargs, int); + break; + + case 's': + s = va_arg (vargs, char*); + args[i].s = s ? strdup (s) : NULL; + break; + } + } +} + +ObjectsBuffer::ObjectCommand::~ObjectCommand () +{ + free (info.fileName); + free (info.completeLine); + + for (int i = 0; i < numArgs; i++) + if (args[i].type == 's' && args[i].s) + free (args[i].s); + + delete[] args; +} + +// ---------------------------------------------------------------------- + +ObjectsBuffer::ObjectsBuffer (ObjectsController *successor) +{ + this->successor = successor; + successor->setObjectsSource (this); + + commandsQueue = new Vector<ObjectCommand> (1, true); + queued = false; + + setObjectsSink (successor); +} + +ObjectsBuffer::~ObjectsBuffer () +{ + delete commandsQueue; +} + +void ObjectsBuffer::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) +{ + process (new ObjectCommand (MSG, info, "ssds", id, aspect, prio, message)); +} + +void ObjectsBuffer::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) +{ + process (new ObjectCommand (MARK, info, "ssds", id, aspect, prio, message)); +} + +void ObjectsBuffer::objMsgStart (CommonLineInfo *info, const char *id) +{ + process (new ObjectCommand (MSG_START, info, "s", id)); +} + +void ObjectsBuffer::objMsgEnd (CommonLineInfo *info, const char *id) +{ + process (new ObjectCommand (MSG_END, info, "s", id)); +} + +void ObjectsBuffer::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args) +{ + process (new ObjectCommand (ENTER, info, "ssdss", id, aspect, prio, funname, + args)); +} + +void ObjectsBuffer::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + process (new ObjectCommand (LEAVE, info, "ss", id, vals)); +} + +void ObjectsBuffer::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + process (new ObjectCommand (CREATE, info, "ss", id, klass)); +} + +void ObjectsBuffer::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + process (new ObjectCommand (IDENT, info, "ss", id1, id2)); +} + +void ObjectsBuffer::objNoIdent (CommonLineInfo *info) +{ + process (new ObjectCommand (NOIDENT, info, "")); +} + +void ObjectsBuffer::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + process (new ObjectCommand (ASSOC, info, "ss", parent, child)); +} + +void ObjectsBuffer::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + process (new ObjectCommand (SET, info, "sss", id, var, val)); +} + +void ObjectsBuffer::objClassColor (CommonLineInfo *info, const char *klass, + const char *color) +{ + process (new ObjectCommand (CLASS_COLOR, info, "ss", klass, color)); +} + +void ObjectsBuffer::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + process (new ObjectCommand (OBJECT_COLOR, info, "ss", id, color)); +} + +void ObjectsBuffer::objDelete (CommonLineInfo *info, const char *id) +{ + process (new ObjectCommand (DELETE, info, "s", id)); +} + +void ObjectsBuffer::queue () +{ + queued = true; +} + +void ObjectsBuffer::pass () +{ + for (int i = 0; i < commandsQueue->size (); i++) + pass (commandsQueue->get (i)); + + commandsQueue->clear (); + queued = false; +} + +void ObjectsBuffer::process (ObjectCommand *command) +{ + if (queued) + queue (command); + else { + pass (command); + delete command; + } +} + +void ObjectsBuffer::queue (ObjectCommand *command) +{ + commandsQueue->put (command); +} + +void ObjectsBuffer::pass (ObjectCommand *command) +{ + CommonLineInfo info = { command->info.fileName, command->info.lineNo, + command->info.processId, + command->info.completeLine }; + ObjectCommand::Arg *a = command->args; + + switch (command->type) { + case MSG: + successor->objMsg (&info, a[0].s, a[1].s, a[2].d, a[3].s); + break; + + case MARK: + successor->objMark (&info, a[0].s, a[1].s, a[2].d, a[3].s); + break; + + case MSG_START: + successor->objMsgStart (&info, a[0].s); + break; + + case MSG_END: + successor->objMsgEnd (&info, a[0].s); + break; + + case ENTER: + successor->objEnter (&info, a[0].s, a[1].s, a[2].d, a[3].s, a[4].s); + break; + + case LEAVE: + successor->objLeave (&info, a[0].s, a[1].s); + break; + + case CREATE: + successor->objCreate (&info, a[0].s, a[1].s); + break; + + case IDENT: + successor->objIdent (&info, a[0].s, a[1].s); + break; + + case NOIDENT: + successor->objNoIdent (&info); + break; + + case ASSOC: + successor->objAssoc (&info, a[0].s, a[1].s); + break; + + case SET: + successor->objSet (&info, a[0].s, a[1].s, a[2].s); + break; + + case CLASS_COLOR: + successor->objClassColor (&info, a[0].s, a[1].s); + break; + + case OBJECT_COLOR: + successor->objObjectColor (&info, a[0].s, a[1].s); + break; + + case DELETE: + successor->objDelete (&info, a[0].s); + break; + } +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objects_buffer.hh b/objects/objects_buffer.hh new file mode 100644 index 0000000..f00ef89 --- /dev/null +++ b/objects/objects_buffer.hh @@ -0,0 +1,88 @@ +#ifndef __OBJECTS_OBJECTS_BUFFER_HH__ +#define __OBJECTS_OBJECTS_BUFFER_HH__ + +#include "objects_parser.hh" +#include "common/tools.hh" + +namespace rtfl { + +namespace objects { + +class ObjectsBuffer: public ObjectsControllerBase +{ +private: + enum CommandType { + MSG, MARK, MSG_START, MSG_END, ENTER, LEAVE, CREATE, IDENT, NOIDENT, + ASSOC, SET, CLASS_COLOR, OBJECT_COLOR, DELETE + }; + + class ObjectCommand: public lout::object::Object + { + friend class ObjectsBuffer; + + private: + CommandType type; + tools::CommonLineInfo info; + + int numArgs; + struct Arg { + char type; + union { + int d; + char *s; + }; + } *args; + + public: + ObjectCommand (CommandType type, tools::CommonLineInfo *info, + const char *fmt, ...); + ~ObjectCommand (); + }; + + ObjectsController *successor; + lout::container::typed::Vector<ObjectCommand> *commandsQueue; + bool queued; + + void process (ObjectCommand *command); + void queue (ObjectCommand *command); + void pass (ObjectCommand *command); + +public: + ObjectsBuffer (ObjectsController *successor); + ~ObjectsBuffer (); + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); + + void queue (); + void pass (); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJECTS_BUFFER_HH__ diff --git a/objects/objects_parser.cc b/objects/objects_parser.cc new file mode 100644 index 0000000..9278b56 --- /dev/null +++ b/objects/objects_parser.cc @@ -0,0 +1,401 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "objects_parser.hh" + +#if 0 +# define PRINT(fmt) printf ("---- [%p] " fmt "\n", this) +# define PRINTF(fmt, ...) printf ("---- [%p] " fmt "\n", this, __VA_ARGS__) +#else +# define PRINT(fmt) +# define PRINTF(fmt, ...) +#endif + +using namespace rtfl::tools; + +namespace rtfl { + +namespace objects { + +namespace timeout { + +inline int getActualType (int type) +{ + return type >> 8; +} + +inline int getCount (int type) +{ + return type & 0xff; +} + +inline int makeType (int actualType, int count) +{ + return count | (actualType << 8); +} + +inline int incType (int type) +{ + return makeType (getActualType (type), getCount (type) + 1); +} + +inline int decType (int type) +{ + return makeType (getActualType (type), getCount (type) - 1); +} + +} // namespace timeout + +ObjectsControllerBase::ObjectsControllerBase() +{ + predessor = NULL; + successor = NULL; +} + +void ObjectsControllerBase::addTimeout (double secs, int type) +{ + PRINTF ("ObjectsControllerBase::addTimeout (%g, %d)", secs, type); + + if (predessor) + predessor->addTimeout (secs, timeout::incType (type)); + else + fprintf (stderr, "addTimeout (%g, %d): no predessor\n", secs, type); +} + +void ObjectsControllerBase::removeTimeout (int type) +{ + if (predessor) + predessor->removeTimeout (timeout::incType (type)); + else + fprintf (stderr, "removeTimeout (%d): no predessor\n", type); +} + +void ObjectsControllerBase::setObjectsSource (ObjectsSource *source) +{ + predessor = source; +} + +void ObjectsControllerBase::timeout (int type) +{ + PRINTF ("ObjectsControllerBase::timeout (%d)", type); + + // We start sending count == 1 to the predessor, so we get back count == 1 + // from it at the end. + if (timeout::getCount (type) == 1) + ownTimeout (timeout::getActualType (type)); + else { + if (successor) + successor->timeout (timeout::decType (type)); + else + fprintf (stderr, "timeout (%d): no successor\n", type); + } +} + +void ObjectsControllerBase::finish () +{ + ownFinish (); + + if (successor) + successor->finish (); +} + +void ObjectsControllerBase::setObjectsSink (ObjectsSink *sink) +{ + successor = sink; +} + +void ObjectsControllerBase::addOwnTimeout (double secs, int type) +{ + PRINTF ("ObjectsControllerBase::addOwnTimeout (%g, %d)", secs, type); + addTimeout (secs, timeout::makeType (0, type)); +} + +void ObjectsControllerBase::removeOwnTimeout (int type) +{ + removeTimeout (timeout::makeType (0, type)); +} + +void ObjectsControllerBase::ownTimeout (int type) +{ + // No implementation. +} + +void ObjectsControllerBase::ownFinish () +{ + // No implementation. +} + +// ---------------------------------------------------------------------- + +ObjectsParser::ObjectsParser (ObjectsController *controller) +{ + this->controller = controller; + source = NULL; + controller->setObjectsSource (this); +} + +ObjectsParser::~ObjectsParser () +{ + controller->setObjectsSource (NULL); +} + +void ObjectsParser::processCommand (CommonLineInfo *info, char *cmd, char *args) +{ + char **parts = NULL; + + if (args == NULL) + // All commands need arguments here. + fprintf (stderr, "Missing arguments:%s\n", info->completeLine); + else if (strcmp (cmd, "obj-msg") == 0) { + parts = split (args, 4); + if (parts[1] && parts[2] && parts[3]) + controller->objMsg (info, parts[0], parts[1], atoi(parts[2]), + parts[3]); + else + fprintf (stderr, "Incomplete line (obj-msg):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-mark") == 0) { + parts = split (args, 4); + if (parts[1] && parts[2] && parts[3]) + controller->objMark (info, parts[0], parts[1], atoi(parts[2]), + parts[3]); + else + fprintf (stderr, "Incomplete line (obj-mark):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-msg-start") == 0) + controller->objMsgStart (info, args); + else if (strcmp (cmd, "obj-msg-end") == 0) + controller->objMsgEnd (info, args); + else if (strcmp (cmd, "obj-enter") == 0) { + parts = split (args, 5); + if (parts[1] && parts[2] && parts[3] && parts[4]) + controller->objEnter (info, parts[0], parts[1], atoi(parts[2]), + parts[3], parts[4]); + else + fprintf (stderr, "Incomplete line (obj-enter):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-leave") == 0) + // Pre-version "obj-leave" does not support values. + controller->objLeave (info, args, NULL); + else if (strcmp (cmd, "obj-create") == 0) { + parts = split (args, 2); + if (parts[1]) + controller->objCreate (info, parts[0], parts[1]); + else + fprintf (stderr, "Incomplete line (obj-create):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-ident") == 0) { + parts = split (args, 2); + if (parts[1]) + controller->objIdent (info, parts[0], parts[1]); + else + fprintf (stderr, "Incomplete line (obj-ident):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-assoc") == 0) { + parts = split (args, 2); + if (parts[1]) + controller->objAssoc (info, parts[0], parts[1]); + else + fprintf (stderr, "Incomplete line (obj-assoc):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-set") == 0) { + parts = split (args, 3); + if (parts[1] && parts[2]) + controller->objSet (info, parts[0], parts[1], parts[2]); + else + fprintf (stderr, "Incomplete line (obj-set):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-color") == 0) { + parts = split (args, 2); + if (parts[1]) { + fprintf (stderr, "Warning: obj-color is deprecated; use " + "obj-class-color instead:\n%s\n", info->completeLine); + controller->objClassColor (info, parts[1], parts[0]); + } else + fprintf (stderr, "Incomplete line (obj-color):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-class-color") == 0) { + parts = split (args, 2); + if (parts[1]) + controller->objClassColor (info, parts[1], parts[0]); + else + fprintf (stderr, "Incomplete line (obj-class-color):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-object-color") == 0) { + parts = split (args, 2); + if (parts[1]) + controller->objObjectColor (info, parts[0], parts[1]); + else + fprintf (stderr, "Incomplete line (obj-object-color):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "obj-delete") == 0) + controller->objDelete (info, args); + else + fprintf (stderr, "Unknown command identifier '%s':\n%s\n", cmd, + info->completeLine); + + if (parts) + freeSplit (parts); +} + +void ObjectsParser::processVCommand (CommonLineInfo *info, const char *module, + int majorVersion, int minorVersion, + const char *cmd, char **args) +{ + if (strcmp (module, "obj") == 0) { + if (majorVersion > 1) + fprintf (stderr, "Last supported version is 1.0:\n%s\n", + info->completeLine); + if (args[0] == NULL) { + if (strcmp (cmd, "noident") == 0) + controller->objNoIdent (info); + else + // All other commands need arguments. + fprintf (stderr, "Missing arguments:%s\n", info->completeLine); + } else if (strcmp (cmd, "msg") == 0) { + if (args[1] && args[2] && args[3]) + controller->objMsg (info, args[0], args[1], atoi(args[2]), args[3]); + else + fprintf (stderr, "Incomplete line (msg):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "mark") == 0) { + if (args[1] && args[2] && args[3]) + controller->objMark (info, args[0], args[1], atoi(args[2]), + args[3]); + else + fprintf (stderr, "Incomplete line (mark):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "msg-start") == 0) + controller->objMsgStart (info, args[0]); + else if (strcmp (cmd, "msg-end") == 0) + controller->objMsgEnd (info, args[0]); + else if (strcmp (cmd, "enter") == 0) { + if (args[1] && args[2] && args[3] && args[4]) + controller->objEnter (info, args[0], args[1], atoi(args[2]), + args[3], args[4]); + else + fprintf (stderr, "Incomplete line (enter):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "leave") == 0) + // Args[1] may be NULL. + controller->objLeave (info, args[0], args[1]); + else if (strcmp (cmd, "create") == 0) { + if (args[1]) + controller->objCreate (info, args[0], args[1]); + else + fprintf (stderr, "Incomplete line (create):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "ident") == 0) { + if (args[1]) + controller->objIdent (info, args[0], args[1]); + else + fprintf (stderr, "Incomplete line (ident):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "assoc") == 0) { + if (args[1]) + controller->objAssoc (info, args[0], args[1]); + else + fprintf (stderr, "Incomplete line (assoc):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "set") == 0) { + if (args[1] && args[2]) + controller->objSet (info, args[0], args[1], args[2]); + else + fprintf (stderr, "Incomplete line (set):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "class-color") == 0) { + if (args[1]) + // Notice the changed order. + controller->objClassColor (info, args[0], args[1]); + else + fprintf (stderr, "Incomplete line (class-color):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "object-color") == 0) { + if (args[1]) + controller->objObjectColor (info, args[0], args[1]); + else + fprintf (stderr, "Incomplete line (object-color):\n%s\n", + info->completeLine); + } else if (strcmp (cmd, "delete") == 0) + controller->objDelete (info, args[0]); + else + fprintf (stderr, "Unknown command identifier '%s':\n%s\n", cmd, + info->completeLine); + } +} + +void ObjectsParser::setLinesSource (LinesSource *source) +{ + this->source = source; +} + +void ObjectsParser::timeout (int type) +{ + PRINTF ("ObjectsParser::timeout (%d)", type); + + if (timeout::getCount (type) == 0) + ; // ownTimeout (timeout::getActualType (type)); + else + controller->timeout (timeout::decType (type)); +} + +void ObjectsParser::finish () +{ + controller->finish (); +} + +void ObjectsParser::setObjectsSink (ObjectsSink *sink) +{ + // This would be the controller passed in the constructor. +} + +void ObjectsParser::addTimeout (double secs, int type) +{ + PRINTF ("ObjectsParser::addTimeout (%g, %d)", secs, type); + + if (source) + source->addTimeout (secs, timeout::incType (type)); + else + fprintf (stderr, "addTimeout (%g, %d): no source\n", secs, type); +} + +void ObjectsParser::removeTimeout (int type) +{ + if (source) + source->removeTimeout (timeout::incType (type)); + else + fprintf (stderr, "removeTimeout (%d): no source\n", type); +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objects_parser.hh b/objects/objects_parser.hh new file mode 100644 index 0000000..e4dd2b0 --- /dev/null +++ b/objects/objects_parser.hh @@ -0,0 +1,115 @@ +#ifndef __OBJECTS_OBJECTS_PARSER_HH__ +#define __OBJECTS_OBJECTS_PARSER_HH__ + +#include "common/parser.hh" +#include "lout/object.hh" + +namespace rtfl { + +namespace objects { + +class ObjectsSink; + +class ObjectsSource +{ +public: + virtual void setObjectsSink (ObjectsSink *sink) = 0; + virtual void addTimeout (double secs, int type) = 0; + virtual void removeTimeout (int type) = 0; +}; + + +class ObjectsSink +{ +public: + virtual void setObjectsSource (ObjectsSource *source) = 0; + virtual void timeout (int type) = 0; + virtual void finish () = 0; +}; + + +class ObjectsController: public lout::object::Object, public ObjectsSource, + public ObjectsSink +{ +public: + virtual void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) = 0; + virtual void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) = 0; + virtual void objMsgStart (tools::CommonLineInfo *info, const char *id) = 0; + virtual void objMsgEnd (tools::CommonLineInfo *info, const char *id) = 0; + virtual void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args) = 0; + virtual void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals) = 0; + virtual void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass) = 0; + virtual void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2) = 0; + virtual void objNoIdent (tools::CommonLineInfo *info) = 0; + virtual void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child) = 0; + virtual void objSet (tools::CommonLineInfo *info, const char *id, + const char *var, const char *val) = 0; + virtual void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color) = 0; + virtual void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color) = 0; + virtual void objDelete (tools::CommonLineInfo *info, const char *id) = 0; +}; + + +class ObjectsControllerBase: public ObjectsController +{ + ObjectsSource *predessor; + ObjectsSink *successor; + +protected: + virtual void ownTimeout (int type); + virtual void ownFinish (); + void addOwnTimeout (double secs, int type); + void removeOwnTimeout (int type); + +public: + ObjectsControllerBase(); + + void setObjectsSink (ObjectsSink *sink); + void addTimeout (double secs, int type); + void removeTimeout (int type); + void setObjectsSource (ObjectsSource *source); + void timeout (int type); + void finish (); +}; + + +class ObjectsParser: public tools::Parser, public ObjectsSource +{ +private: + ObjectsController *controller; + tools::LinesSource *source; + +protected: + void processCommand (tools::CommonLineInfo *info, char *cmd, char *args); + void processVCommand (tools::CommonLineInfo *info, const char *module, + int majorVersion, int minorVersion, const char *cmd, + char **args); + +public: + ObjectsParser (ObjectsController *controller); + ~ObjectsParser (); + + void setLinesSource (tools::LinesSource *source); + void timeout (int type); + void finish (); + + void setObjectsSink (ObjectsSink *sink); + void addTimeout (double secs, int type); + void removeTimeout (int type); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_PARSER_HH__ diff --git a/objects/objects_writer.cc b/objects/objects_writer.cc new file mode 100644 index 0000000..b9c2581 --- /dev/null +++ b/objects/objects_writer.cc @@ -0,0 +1,145 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objects_writer.hh" + +#define DBG_RTFL + +#include "debug_rtfl.hh" + +using namespace rtfl::tools; + +// If foreign messages ("F" stands for "foreign") are passed, the original +// filename, line number and process id must be printed. + +#define F_RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, info->fileName, info->lineNo, info->processId, \ + "s:" fmt, cmd, __VA_ARGS__) + +#define F_RTFL_PRINT0(module, version, cmd) \ + rtfl_print (module, version, info->fileName, info->lineNo, info->processId, \ + "s", cmd) + +#define F_RTFL_OBJ_PRINT(cmd, fmt, ...) \ + F_RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#define F_RTFL_OBJ_PRINT0(cmd) \ + F_RTFL_PRINT0 ("obj", RTFL_OBJ_VERSION, cmd) + +namespace rtfl { + +namespace objects { + +void ObjectsWriter::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) +{ + F_RTFL_OBJ_PRINT ("msg", "s:s:d:s", id, aspect, prio, message); +} + +void ObjectsWriter::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message) +{ + F_RTFL_OBJ_PRINT ("mark", "s:s:d:s", id, aspect, prio, message); +} + +void ObjectsWriter::objMsgStart (CommonLineInfo *info, const char *id) +{ + F_RTFL_OBJ_PRINT ("msg-start", "s", id); +} + +void ObjectsWriter::objMsgEnd (CommonLineInfo *info, const char *id) +{ + F_RTFL_OBJ_PRINT ("msg-end", "s", id); +} + +void ObjectsWriter::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args) +{ + F_RTFL_OBJ_PRINT ("enter", "s:s:d:s:s", id, aspect, prio, funname, args); +} + +void ObjectsWriter::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + if (vals) + F_RTFL_OBJ_PRINT ("leave", "s:s", id, vals); + else + F_RTFL_OBJ_PRINT ("leave", "s", id); +} + +void ObjectsWriter::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + F_RTFL_OBJ_PRINT ("create", "s:s", id, klass); +} + +void ObjectsWriter::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + F_RTFL_OBJ_PRINT ("create", "s:s", id1, id2); +} + +void ObjectsWriter::objNoIdent (CommonLineInfo *info) +{ + F_RTFL_OBJ_PRINT0 ("noident"); +} + +void ObjectsWriter::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + F_RTFL_OBJ_PRINT ("assoc", "s:s", parent, child); +} + +void ObjectsWriter::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + F_RTFL_OBJ_PRINT ("set", "s:s:s", id, var, val); +} + +void ObjectsWriter::objClassColor (CommonLineInfo *info, const char *klass, + const char *color) +{ + F_RTFL_OBJ_PRINT ("class-color", "s:s", klass, color); +} + +void ObjectsWriter::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + F_RTFL_OBJ_PRINT ("object-color", "s:s", id, color); +} + +void ObjectsWriter::objDelete (CommonLineInfo *info, const char *id) +{ + F_RTFL_OBJ_PRINT ("delete", "s", id); +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objects_writer.hh b/objects/objects_writer.hh new file mode 100644 index 0000000..80e629e --- /dev/null +++ b/objects/objects_writer.hh @@ -0,0 +1,44 @@ +#ifndef __OBJECTS_OBJECTS_WRITER_HH__ +#define __OBJECTS_OBJECTS_WRITER_HH__ + +#include "objects_parser.hh" + +namespace rtfl { + +namespace objects { + +class ObjectsWriter: public ObjectsControllerBase +{ +public: + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_WRITER_HH__ diff --git a/objects/objident_controller.cc b/objects/objident_controller.cc new file mode 100644 index 0000000..deaf2fd --- /dev/null +++ b/objects/objident_controller.cc @@ -0,0 +1,339 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include "objident_controller.hh" + +using namespace lout::object; +using namespace lout::misc; +using namespace lout::container::untyped; +using namespace rtfl::tools; + +#define TIMEOUT_SECS 1 + +namespace rtfl { + +namespace objects { + +ObjIdentController::PostController::PostController (ObjectsController + *successor) +{ + this->successor = successor; + successor->setObjectsSource (this); + setObjectsSink (successor); + + // The "canonocal" identity is stored both as key and as value. For + // this reason, the second argument could be "false". Check whether + // this is possible (order of delete etc.). + identities = new EquivalenceRelation (true, true); +} + +ObjIdentController::PostController::~PostController () +{ + delete identities; +} + +void ObjIdentController::PostController::objMsg (CommonLineInfo *info, + const char *id, + const char *aspect, int prio, + const char *message) +{ + successor->objMsg (info, mapId (id), aspect, prio, message); +} + +void ObjIdentController::PostController::objMark (CommonLineInfo *info, + const char *id, + const char *aspect, int prio, + const char *message) +{ + successor->objMark (info, mapId (id), aspect, prio, message); +} + +void ObjIdentController::PostController::objMsgStart (CommonLineInfo *info, + const char *id) +{ + successor->objMsgStart (info, mapId (id)); +} + +void ObjIdentController::PostController::objMsgEnd (CommonLineInfo *info, + const char *id) +{ + successor->objMsgEnd (info, mapId (id)); +} + +void ObjIdentController::PostController::objEnter (CommonLineInfo *info, + const char *id, + const char *aspect, int prio, + const char *funname, + const char *args) +{ + successor->objEnter (info, mapId (id), aspect, prio, funname, args); +} + +void ObjIdentController::PostController::objLeave (CommonLineInfo *info, + const char *id, + const char *vals) +{ + successor->objLeave (info, mapId (id), vals); +} + +void ObjIdentController::PostController::objCreate (CommonLineInfo *info, + const char *id, + const char *klass) +{ + successor->objCreate (info, mapId (id), klass); +} + +void ObjIdentController::PostController::objIdent (CommonLineInfo *info, + const char *id1, + const char *id2) +{ + // "obj-ident" is not delegated. +} + +void ObjIdentController::PostController::objNoIdent (CommonLineInfo *info) +{ + // "obj-noident" is not delegated. +} + +void ObjIdentController::PostController::objAssoc (CommonLineInfo *info, + const char *parent, + const char *child) +{ + successor->objAssoc (info, mapId (parent), mapId (child)); +} + +void ObjIdentController::PostController::objSet (CommonLineInfo *info, + const char *id, + const char *var, + const char *val) +{ + successor->objSet (info, mapId (id), var, val); +} + +void ObjIdentController::PostController::objClassColor (CommonLineInfo *info, + const char *klass, + const char *color) +{ + successor->objClassColor (info, klass, color); +} + +void ObjIdentController::PostController::objObjectColor (CommonLineInfo *info, + const char *id, + const char *color) +{ + successor->objObjectColor (info, mapId (id), color); +} + +void ObjIdentController::PostController::objDelete (CommonLineInfo *info, + const char *id) +{ + successor->objDelete (info, mapId (id)); + + // TODO Remove from identities. +} + +void ObjIdentController::PostController::addIdentity (const char *id1, + const char *id2) +{ + String key1 (id1), key2 (id2); + + if (!identities->contains (&key1)) + identities->put (new String (id1), new String (id1)); + + if (!identities->contains (&key2)) + identities->put (new String (id2), new String (id2)); + + identities->relate (&key1, &key2); +} + +const char *ObjIdentController::PostController::mapId (const char *id) +{ + String key (id); + if (!identities->contains (&key)) + identities->put (new String (id), new String (id)); + + return ((String*)identities->get(&key))->chars (); +} + +// ---------------------------------------------------------------------- + +ObjIdentController::ObjIdentController (ObjectsController *successor) +{ + noIdent = false; + stackDepth = 0; + createPending = false; + + postController = new PostController (successor); + buffer = new ObjectsBuffer (postController); + buffer->setObjectsSource (this); + setObjectsSink (buffer); +} + +ObjIdentController::~ObjIdentController () +{ +} + +void ObjIdentController::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + buffer->objMsg (info, id, aspect, prio, message); +} + +void ObjIdentController::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + buffer->objMark (info, id, aspect, prio, message); +} + +void ObjIdentController::objMsgStart (CommonLineInfo *info, const char *id) +{ + buffer->objMsgStart (info, id); +} + +void ObjIdentController::objMsgEnd (CommonLineInfo *info, const char *id) +{ + buffer->objMsgEnd (info, id); +} + +void ObjIdentController::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *funname, const char *args) +{ + stackDepth++; + + buffer->objEnter (info, id, aspect, prio, funname, args); +} + +void ObjIdentController::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + stackDepth = max (stackDepth - 1, 0); + + if (createPending && stackDepth < minCreateStackDepth) { + pass (); + removeOwnTimeout (PASS); + } + + buffer->objLeave (info, id, vals); +} + +void ObjIdentController::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + queue (); + buffer->objCreate (info, id, klass); +} + +void ObjIdentController::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + postController->addIdentity (id1, id2); + + // TODO: Possibly end queueing? Probably not. + + // "obj-ident" is not delegated. +} + +void ObjIdentController::objNoIdent (CommonLineInfo *info) +{ + noIdent = true; + pass (); + + // "obj-noident" is not delegated. +} + +void ObjIdentController::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + buffer->objAssoc (info, parent, child); +} + +void ObjIdentController::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + buffer->objSet (info, id, var, val); +} + +void ObjIdentController::objClassColor (CommonLineInfo *info, const char *klass, + const char *color) +{ + buffer->objClassColor (info, klass, color); +} + +void ObjIdentController::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + buffer->objObjectColor (info, id, color); +} + +void ObjIdentController::objDelete (CommonLineInfo *info, const char *id) +{ + buffer->objDelete (info, id); +} + +void ObjIdentController::ownTimeout (int type) +{ + if (type == PASS) + pass (); +} + +void ObjIdentController::ownFinish () +{ + pass (); +} + +void ObjIdentController::queue () +{ + if (!noIdent) { + if (createPending) + minCreateStackDepth = min (minCreateStackDepth, stackDepth); + else { + buffer->queue (); + createPending = true; + minCreateStackDepth = stackDepth; + } + + removeOwnTimeout (PASS); + addOwnTimeout (TIMEOUT_SECS, PASS); + } +} + +void ObjIdentController::pass () +{ + buffer->pass (); + createPending = false; +} + +} // namespace objects + +} // namespace rtfl + diff --git a/objects/objident_controller.hh b/objects/objident_controller.hh new file mode 100644 index 0000000..2d83b20 --- /dev/null +++ b/objects/objident_controller.hh @@ -0,0 +1,192 @@ +#ifndef __OBJECTS_OBJIDENT_CONTROLLER_HH__ +#define __OBJECTS_OBJIDENT_CONTROLLER_HH__ + +#include "objects_parser.hh" +#include "objects_buffer.hh" +#include "common/tools.hh" + +namespace rtfl { + +namespace objects { + +/** + * \ident Filter Controller for handling `obj-ident` + * + * Motivation + * ---------- + * When using multiple inheritance in C++, the values for `this` are not + * identical in all contexts. Consider a class `C`, with `A` and `B` as base + * classes. The following code + * + * C *c = new C(); + * A *a = (A*)c; + * B *b = (B*)c; + * printf("a = %p\n", a); + * printf("b = %p\n", b); + * printf("c = %p\n", c); + * + * will output something like this: + * + * a = 0x1000 + * b = 0x1010 + * c = 0x1000 + * + * (Notice the different value of `b`.) The value of `b` will differ from `a` by + * `sizeof(A)`. + * + * For this reason, the tested program has to declare all values as identical, + * in the following way: + * + * [rtfl-obj-1.0]...:create:0x1000:A + * [rtfl-obj-1.0]...:create:0x1010:B + * [rtfl-obj-1.0]...:ident:0x1000:0x1000 + * [rtfl-obj-1.0]...:ident:0x1000:0x1010 + * [rtfl-obj-1.0]...:create:0x1000:C + * + * The first `ident` line declares `c` (the newly created instance of `C` as + * identical to `a` (`(A*)c`), the second declares it identical to `b` + * (`(B*)c`). + * + * General Approach + * ---------------- + * Handling `obj-ident` is kept out of the specific implementations of + * `ObjectsController` and implemented in a filter, `ObjIdentController`, which + * will translate all identities to actually identical identities; the output of + * `ObjIdentController` would, in the example above, be: + * + * [rtfl-obj-1.0]...:create:0x1000:A + * [rtfl-obj-1.0]...:create:0x1000:B + * [rtfl-obj-1.0]...:create:0x1000:C + * + * Details + * ------- + * The general problem is that some messages have to be changed (especially + * `...:create:0x1010:B` has to be changed to `...:create:0x1000:B`) before + * `obj-ident` is read. Even worse, it is not certain that `obj-ident` will + * follow at all: the second `obj-create` may actually define a _different_ + * object. For this reason, starting with `obj-create`, all messages are held + * back until something happens that makes it certain that _no_ related + * `obj-ident` will follow. + * + * Notice that a functioning implementation is possible even if the second + * condition is incompletely defined and implemented. + * + * (For more complex class hierarchies, it is not sufficient to react on + * `obj-ident` itself, since another `obj-ident` may follow, dealing with the + * same identities.) + * + * When is it certain that no related `obj-ident` will follow? + * + * 1. When a method is left (`obj-leave`), in which the objects have been + * created. + * 2. At the end of the stream. + * 3. (Not an exact condition, but a compromise:) After a timeout of a couple of + * seconds; after some time, it can be assumed that the construction of + * objects is over. + * + * (May be extended. See rtfl::objects::ObjIdentController::objLeave, + * rtfl::objects::ObjIdentController::ownTimeout, and + * rtfl::objects::ObjIdentController::ownFinish. + * + * Nice to have + * ------------ + * - Make timeouts configurable, also whether a timeout is triggered after a + * certain time after "obj-create" or a certain time after no commands (latter + * is currently implemented). + */ +class ObjIdentController: public ObjectsControllerBase +{ +private: + class PostController: public ObjectsControllerBase + { + tools::EquivalenceRelation *identities; + ObjectsController *successor; + + const char *mapId (const char *id); + + public: + PostController (ObjectsController *successor); + ~PostController (); + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); + + void addIdentity (const char *id1, const char *id2); + }; + + enum { PASS = 0 }; + + ObjectsBuffer *buffer; + PostController *postController; + bool noIdent; + + int stackDepth; + bool createPending; + int minCreateStackDepth; + + void queue (); + void pass (); + +protected: + void ownTimeout (int type); + void ownFinish (); + +public: + ObjIdentController (ObjectsController *successor); + ~ObjIdentController (); + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJIDENT_CONTROLLER_HH__ diff --git a/objects/objview_commands.cc b/objects/objview_commands.cc new file mode 100644 index 0000000..81d03aa --- /dev/null +++ b/objects/objview_commands.cc @@ -0,0 +1,773 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objview_graph.hh" // Because ObjViewGraph is needed here. + +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; +using namespace rtfl::dw; + +namespace rtfl { + +namespace objects { + +static void scrollToLabel (Label *label) +{ + Widget *parent = label->getParent (), *child = label; + + while (parent != NULL && !parent->instanceOf (ObjViewGraph::CLASS_ID)) { + if (parent->instanceOf (Toggle::CLASS_ID)) { + Toggle *toggle = (Toggle*)parent; + if (toggle->isLargeShown () && child == toggle->getSmall ()) + toggle->toggle (false); + else if (!toggle->isLargeShown () && child == toggle->getLarge ()) + toggle->toggle (true); + } + + child = parent; + parent = parent->getParent (); + } + + label->scrollTo (HPOS_INTO_VIEW, VPOS_INTO_VIEW); +} + +// ---------------------------------------------------------------------- + +bool OVGCommonCommand::CommandLinkReceiver::click (Widget *widget, int link, + int img, int x, int y, + EventButton *event) +{ + // "link" is "navigableCommandsIndex". + graph->setCommand (link); + return true; +} + +// ---------------------------------------------------------------------- + +OVGCommonCommand::OVGCommonCommand (ObjViewGraph *graph, const char *id, + ObjViewFunction *function) +{ + this->id = strdup (id); + this->graph = graph; + this->function = function; + relatedCommand = NULL; + executed = false; + visible = true; +} + +OVGCommonCommand::~OVGCommonCommand () +{ + free (id); +} + +const char *OVGCommonCommand::getFileName () +{ + // Not needed for non-navigable command. + assertNotReached (); + return NULL; +} + +int OVGCommonCommand::getLineNo () +{ + // Not needed for non-navigable command. + assertNotReached (); + return 0; +} + +int OVGCommonCommand::getProcessId () +{ + // Not yet clear what to do with this one. + assertNotReached (); + return 0; +} + +void OVGCommonCommand::exec (ObjViewGraph *graph) +{ + assert (!executed); + assert (graph == this->graph); + doExec (); + executed = true; +} + +void OVGCommonCommand::show (ObjViewGraph *graph) +{ + assert (executed); + assert (graph == this->graph); + visible = true; + doShow (); +} + +void OVGCommonCommand::hide (ObjViewGraph *graph) +{ + assert (executed); + assert (graph == this->graph); + visible = false; + doHide (); +} + +void OVGCommonCommand::scrollTo (ObjViewGraph *graph) +{ + assert (executed); + assert (graph == this->graph); + doScrollTo (); +} + +void OVGCommonCommand::select (ObjViewGraph *graph) +{ + assert (executed); + assert (graph == this->graph); + doSelect (); +} + +void OVGCommonCommand::unselect (ObjViewGraph *graph) +{ + assert (executed); + assert (graph == this->graph); + doUnselect (); +} + +// Most commands do not have to impelemt these: + +void OVGCommonCommand::doShow () +{ + assertNotReached (); +} + +void OVGCommonCommand::doHide () +{ + assertNotReached (); +} + +void OVGCommonCommand::doScrollTo () +{ + assertNotReached (); +} + +void OVGCommonCommand::doSelect () +{ + assertNotReached (); +} + +void OVGCommonCommand::doUnselect () +{ + assertNotReached (); +} + +bool OVGCommonCommand::isVisible (ObjViewGraph *graph) +{ + return visible; +} + +bool OVGCommonCommand::calcVisibility (ObjViewGraph *graph) +{ + return true; +} + +int OVGCommonCommand::getNavigableCommandsIndex (ObjViewGraph *graph) +{ + assertNotReached (); + return -1; +} + +void OVGCommonCommand::setNavigableCommandsIndex (ObjViewGraph *graph, + int navigableCommandsIndex) +{ + assertNotReached (); +} + +void OVGCommonCommand::setVisibleIndex (ObjViewGraph *graph, + int visibleIndex) +{ + assertNotReached (); +} + +// ---------------------------------------------------------------------- + +OVGNavigableCommand::OVGNavigableCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + ObjViewFunction *function) : + OVGCommonCommand (graph, id, function) +{ + this->fileName = strdup (fileName); + this->lineNo = lineNo; + + navigableCommandsIndex = -1; + label1 = label2 = NULL; + hbox = NULL; +} + +OVGNavigableCommand::~OVGNavigableCommand () +{ + free (fileName); +} + +const char *OVGNavigableCommand::getFileName () +{ + return fileName; +} + +int OVGNavigableCommand::getLineNo () +{ + return lineNo; +} + +void OVGNavigableCommand::initWidgets () +{ + label1->setLink (navigableCommandsIndex); + label1->connectLink (&linkReceiver); + label2->setLink (navigableCommandsIndex); + label2->connectLink (&linkReceiver); +} + +void OVGNavigableCommand::removeWidgets () +{ + delete hbox; + + label1 = label2 = NULL; + hbox = NULL; +} + +int OVGNavigableCommand::getNavigableCommandsIndex (ObjViewGraph *graph) +{ + return navigableCommandsIndex; +} + +void OVGNavigableCommand::setNavigableCommandsIndex (ObjViewGraph *graph, + int navigableCommandsIndex) +{ + this->navigableCommandsIndex = navigableCommandsIndex; + if (label1) + label1->setLink (navigableCommandsIndex); + if (label2) + label2->setLink (navigableCommandsIndex); +} + +void OVGNavigableCommand::setVisibleIndex (ObjViewGraph *graph, + int visibleIndex) +{ + assert (label1 != NULL); + + // Assume 5 digits (99999) at max. + char buf[15]; + snprintf (buf, 15, "<i>%d:</i> ", visibleIndex); + label1->setText (buf); +} + +void OVGNavigableCommand::doShow () +{ + label1->show (); + label2->show (); +} + +void OVGNavigableCommand::doHide () +{ + label1->hide (); + label2->hide (); +} + +void OVGNavigableCommand::doScrollTo () +{ + scrollToLabel (label2); +} + +void OVGNavigableCommand::doSelect () +{ + label1->select (); + label2->select (); +} + +void OVGNavigableCommand::doUnselect () +{ + label1->unselect (); + label2->unselect (); +} + +// ---------------------------------------------------------------------- + +OVGIncIndentCommand::OVGIncIndentCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); +} + +OVGIncIndentCommand::~OVGIncIndentCommand () +{ +} + +bool OVGIncIndentCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_INDENT); +} + +void OVGIncIndentCommand::doExec () +{ + graph->addMessage (id, "<i>start</i>", &hbox, &label1, &label2); + initWidgets (); + success = graph->incIndent (id); +} + +// ---------------------------------------------------------------------- + +OVGDecIndentCommand::OVGDecIndentCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + OVGIncIndentCommand *startCommand, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); + + setRelatedCommand (startCommand); + startCommand->setRelatedCommand (this); +} + +OVGDecIndentCommand::~OVGDecIndentCommand () +{ +} + +bool OVGDecIndentCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_INDENT); +} + +void OVGDecIndentCommand::doExec () +{ + success = graph->decIndent (id); + graph->addMessage (id, "<i>end</i>", &hbox, &label1, &label2); + initWidgets (); +} + +// ---------------------------------------------------------------------- + +OVGEnterCommand::OVGEnterCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + const char *aspect, int prio, + const char *funname, const char *args, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); + + graph->filterTool->addAspect (aspect); + graph->filterTool->addPriority (prio); + + this->aspect = strdup (aspect); + this->prio = prio; + this->funname = strdup (funname); + this->args = strdup (args); +} + +OVGEnterCommand::~OVGEnterCommand () +{ + free (aspect); + free (funname); + free (args); +} + +bool OVGEnterCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_FUNCTION) && + graph->filterTool->isAspectSelected (aspect) && + graph->filterTool->isPrioritySelected (prio); +} + +void OVGEnterCommand::doExec () +{ + char *message = createMessage ("<i>enter</i>: <b>", "</b> (", ")", ""); + graph->addMessage (id, message, &hbox, &label1, &label2); + freeMessage (message); + initWidgets (); + + success = graph->incIndent (id); +} + +char *OVGEnterCommand::createMessage (const char *c1, const char *c2, + const char *c3a, const char *c3b) +{ + size_t l = strlen (c1) + strlen (funname) + strlen (c2) + strlen (args) + + strlen (c3a) + strlen (c3b) + 1; + char *message = new char[l]; + snprintf (message, l, "%s%s%s%s%s%s", c1, funname, c2, args, c3a, c3b); + return message; +} + +void OVGEnterCommand::freeMessage (char *message) +{ + delete[] message; +} + +char *OVGEnterCommand::createName () +{ + size_t l = + 1 + strlen (id) + 2 + strlen (funname) + 2 + strlen (args) + 1 + 1; + char *message = new char[l]; + snprintf (message, l, "[%s] %s (%s)", id, funname, args); + return message; +} + +void OVGEnterCommand::freeName (char *name) +{ + delete[] name; +} + +int OVGEnterCommand::getColor () +{ + return graph->getObjectColor (id); +} + +ObjViewFunction *OVGEnterCommand::getParent () +{ + return getFunction (); +} + +bool OVGEnterCommand::isSelectable () +{ + return isVisible (graph); +} + +void OVGEnterCommand::select () +{ + graph->clearSelection (); + graph->navigableCommandsPos = navigableCommandsIndex; + graph->unclearSelection (); +} + +// ---------------------------------------------------------------------- + +OVGLeaveCommand::OVGLeaveCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + const char *vals, + OVGEnterCommand *enterCommand) : + OVGNavigableCommand (fileName, lineNo, graph, id, enterCommand) +{ + linkReceiver.setData (graph, this); + + this->enterCommand = enterCommand; + this->vals = vals ? strdup (vals) : NULL; + + setRelatedCommand (enterCommand); + enterCommand->setRelatedCommand (this); +} + +OVGLeaveCommand::~OVGLeaveCommand () +{ + if (vals) + free (vals); +} + +bool OVGLeaveCommand::calcVisibility (ObjViewGraph *graph) +{ + return enterCommand->calcVisibility (graph); +} + +void OVGLeaveCommand::doExec () +{ + success = graph->decIndent (id); + + char *message = vals ? + enterCommand->createMessage ("<i>leave: ", "</i> (", ") ⇒ ", vals) : + enterCommand->createMessage ("<i>leave: ", "</i> (", ")", ""); + + graph->addMessage (id, message, &hbox, &label1, &label2); + enterCommand->freeMessage (message); + initWidgets (); +} + +// ---------------------------------------------------------------------- + +OVGAddMessageCommand::OVGAddMessageCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, + const char *id, const char *aspect, + int prio, const char *message, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); + + graph->filterTool->addAspect (aspect); + graph->filterTool->addPriority (prio); + + this->aspect = strdup (aspect); + this->prio = prio; + this->message = strdup (message); +} + +OVGAddMessageCommand::~OVGAddMessageCommand () +{ + free (aspect); + free (message); +} + +bool OVGAddMessageCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_MESSAGE) && + graph->filterTool->isAspectSelected (aspect) && + graph->filterTool->isPrioritySelected (prio); +} + +void OVGAddMessageCommand::doExec () +{ + graph->addMessage (id, message, &hbox, &label1, &label2); + initWidgets (); +} + +// ---------------------------------------------------------------------- + +OVGAddMarkCommand::OVGAddMarkCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, + const char *id, const char *aspect, + int prio, const char *mark, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); + + graph->filterTool->addAspect (aspect); + graph->filterTool->addPriority (prio); + + this->aspect = strdup (aspect); + this->prio = prio; + this->mark = strdup (mark); +} + +OVGAddMarkCommand::~OVGAddMarkCommand () +{ + free (aspect); + free (mark); +} + +bool OVGAddMarkCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_MARK) && + graph->filterTool->isAspectSelected (aspect) && + graph->filterTool->isPrioritySelected (prio); +} + +void OVGAddMarkCommand::doExec () +{ + size_t l = 13 + strlen (mark) + 1; + char *message = new char[l]; + snprintf (message, l, "<i>mark:</i> %s", mark); + + graph->addMessage (id, message, &hbox, &label1, &label2); + initWidgets (); + + delete[] message; +} + +bool OVGAddMarkCommand::isSelectable () +{ + return isVisible (graph); +} + +void OVGAddMarkCommand::select () +{ + graph->clearSelection (); + graph->navigableCommandsPos = navigableCommandsIndex; + graph->unclearSelection (); +} + +// ---------------------------------------------------------------------- + +OVGCreateCommand::OVGCreateCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + const char *className, + ObjViewFunction *function): + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + this->className = strdup (className); + linkReceiver.setData (graph, this); +} + +OVGCreateCommand::~OVGCreateCommand () +{ + free (className); +} + +bool OVGCreateCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_CREATE); +} + +void OVGCreateCommand::doExec () +{ + size_t l = 15 + strlen (className) + 1; + char *message = new char[l]; + snprintf (message, l, "<i>create:</i> %s", className); + + graph->addMessage (id, message, &hbox, &label1, &label2); + initWidgets (); + + delete[] message; + + graph->setClassName (id, className); +} + +// ---------------------------------------------------------------------- + +OVGAddAssocCommand::OVGAddAssocCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, + const char *id1, const char *id2, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id1, function) +{ + linkReceiver.setData (graph, this); + this->id2 = strdup (id2); +} + +OVGAddAssocCommand::~OVGAddAssocCommand () +{ + free (id2); +} + +bool OVGAddAssocCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_ASSOC); +} + +void OVGAddAssocCommand::doExec () +{ + size_t l = 14 + strlen (id2) + 5 + 1; + char *message = new char[l]; + snprintf (message, l, "<i>assoc → %s</i>", id2); + + graph->addMessage (id, message, &hbox, &label1, &label2); + initWidgets (); + + delete[] message; + + graph->addAssoc (id, id2); +} + +// ---------------------------------------------------------------------- + +OVGAddAttrCommand::OVGAddAttrCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + const char *name,const char *value, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); + + this->name = strdup (name); + this->value = strdup (value); + + smallLabel = NULL; + attribute = NULL; + childNo = -1; +} + +OVGAddAttrCommand::~OVGAddAttrCommand () +{ + free (name); + free (value); +} + +bool OVGAddAttrCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_ADD_ATTR); +} + +void OVGAddAttrCommand::doExec () +{ + attribute = graph->addAttribute (id, name, value, &smallLabel, NULL, &label1, + &label2); + childNo = attribute->registerChild (); + initWidgets (); +} + +void OVGAddAttrCommand::doShow () +{ + OVGNavigableCommand::doShow (); + + // Set the appropriate current value (seen when the history is + // hidden). It can be assumed that doShow() is called in the order + // of the creation, so the last visible command will stay; this + // does indeed represent the current value. + assert (smallLabel != NULL); + smallLabel->setText (value); + + // ... + assert (attribute != NULL); + assert (childNo != -1); + attribute->childShown (childNo); +} + +void OVGAddAttrCommand::doHide () +{ + OVGNavigableCommand::doHide (); + + // ... + assert (attribute != NULL); + assert (childNo != -1); + attribute->childHidden (childNo); +} + +// ---------------------------------------------------------------------- + +OVGDeleteCommand::OVGDeleteCommand (const char *fileName, int lineNo, + ObjViewGraph *graph, const char *id, + ObjViewFunction *function) : + OVGNavigableCommand (fileName, lineNo, graph, id, function) +{ + linkReceiver.setData (graph, this); +} + +OVGDeleteCommand::~OVGDeleteCommand () +{ +} + +bool OVGDeleteCommand::calcVisibility (ObjViewGraph *graph) +{ + return graph->filterTool->isTypeSelected (OVG_COMMAND_DELETE); +} + +void OVGDeleteCommand::doExec () +{ + graph->addMessage (id, "<i>delete</i>", &hbox, &label1, &label2); + initWidgets (); + + // Change appearance of window (simple experiment). + //Widget *node = graph->ensureObject(id)->node; + //StyleAttrs attrs = *(node->getStyle()); + //attrs.setBorderStyle (BORDER_DOTTED); + //node->setStyle (Style::create (&attrs)); + + // Furthermore, this message is added often several times, since this command + // is printed in all destructors in the class hierarchie. Should be limited + // to once. +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objview_commands.hh b/objects/objview_commands.hh new file mode 100644 index 0000000..8e81e99 --- /dev/null +++ b/objects/objview_commands.hh @@ -0,0 +1,349 @@ +#ifndef __OBJECTS_OBJVIEW_COMMANDS_HH__ +#define __OBJECTS_OBJVIEW_COMMANDS_HH__ + +#include "objview_stacktrace.hh" +#include "dwr/toggle.hh" +#include "dwr/vbox.hh" +#include "dwr/hbox.hh" +#include "dwr/label.hh" + +namespace rtfl { + +namespace objects { + +class ObjViewGraph; +class OVGAttribute; + +/* + * TODO: OVGCommand was originally introduced to provide a undo/redo mechanism + * needed for handling "obj-ident". Now, after "obj-ident" is handled by + * ObjIdentController, OVGCommand can be simplified and reduced to navigable + * commands. + */ + +class OVGCommand: public lout::object::Object +{ +public: + virtual const char *getFileName () = 0; + virtual int getLineNo () = 0; + virtual int getProcessId () = 0; + + virtual void exec (ObjViewGraph *graph) = 0; + virtual void show (ObjViewGraph *graph) = 0; + virtual void hide (ObjViewGraph *graph) = 0; + virtual void scrollTo (ObjViewGraph *graph) = 0; + virtual void select (ObjViewGraph *graph) = 0; + virtual void unselect (ObjViewGraph *graph) = 0; + virtual bool isVisible (ObjViewGraph *graph) = 0; + virtual bool calcVisibility (ObjViewGraph *graph) = 0; + virtual int getNavigableCommandsIndex (ObjViewGraph *graph) = 0; + virtual void setNavigableCommandsIndex (ObjViewGraph *graph, + int navigableCommandsIndex) = 0; + virtual void setVisibleIndex (ObjViewGraph *graph, + int visibleIndex) = 0; + virtual ObjViewFunction *getFunction () = 0; + virtual void setRelatedCommand (OVGCommand *relatedCommand) = 0; + virtual OVGCommand *getRelatedCommand () = 0; +}; + + +class OVGCommonCommand: public OVGCommand +{ +private: + bool executed, visible; + ObjViewFunction *function; + OVGCommand *relatedCommand; + +protected: + class CommandLinkReceiver: public ::dw::core::Layout::LinkReceiver + { + private: + ObjViewGraph *graph; + OVGCommand *command; + + public: + inline CommandLinkReceiver () { graph = NULL; } + inline void setData (ObjViewGraph *graph, + OVGCommand *command) + { this->graph = graph; this->command = command; } + + bool click (::dw::core::Widget *widget, int link, int img, int x, int y, + ::dw::core::EventButton *event); + }; + + char *id; + ObjViewGraph *graph; + + virtual void doExec () = 0; + virtual void doShow (); + virtual void doHide (); + virtual void doScrollTo (); + virtual void doSelect (); + virtual void doUnselect (); + +public: + OVGCommonCommand (ObjViewGraph *graph, const char *id, + ObjViewFunction *function); + ~OVGCommonCommand (); + + const char *getFileName (); + int getLineNo (); + int getProcessId (); + + void exec (ObjViewGraph *graph); + void show (ObjViewGraph *graph); + void hide (ObjViewGraph *graph); + void scrollTo (ObjViewGraph *graph); + void select (ObjViewGraph *graph); + void unselect (ObjViewGraph *graph); + bool isVisible (ObjViewGraph *graph); + bool calcVisibility (ObjViewGraph *graph); + int getNavigableCommandsIndex (ObjViewGraph *graph); + void setNavigableCommandsIndex (ObjViewGraph *graph, + int navigableCommandsIndex); + void setVisibleIndex (ObjViewGraph *graph, int visibleIndex); + ObjViewFunction *getFunction () { return function; } + void setRelatedCommand (OVGCommand *relatedCommand) + { this->relatedCommand = relatedCommand; } + OVGCommand *getRelatedCommand () { return relatedCommand; } +}; + + +class OVGNavigableCommand: public OVGCommonCommand +{ +private: + char *fileName; + int lineNo; + +protected: + int navigableCommandsIndex; + dw::HBox *hbox; + dw::Label *label1, *label2; + CommandLinkReceiver linkReceiver; + + void doShow (); + void doHide (); + void doScrollTo (); + void doSelect (); + void doUnselect (); + + void initWidgets (); + void removeWidgets (); + +public: + OVGNavigableCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, ObjViewFunction *function); + ~OVGNavigableCommand (); + + const char *getFileName (); + int getLineNo (); + + int getNavigableCommandsIndex (ObjViewGraph *graph); + void setNavigableCommandsIndex (ObjViewGraph *graph, + int navigableCommandsIndex); + void setVisibleIndex (ObjViewGraph *graph, int visibleIndex); +}; + +class OVGIncIndentCommand: public OVGNavigableCommand +{ +private: + bool success; + +protected: + void doExec (); + +public: + OVGIncIndentCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, ObjViewFunction *function); + ~OVGIncIndentCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + + +class OVGDecIndentCommand: public OVGNavigableCommand +{ +private: + bool success; + +protected: + void doExec (); + +public: + OVGDecIndentCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, OVGIncIndentCommand *startCommand, + ObjViewFunction *function); + ~OVGDecIndentCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + +class OVGEnterCommand: public OVGNavigableCommand, public ObjViewFunction +{ +private: + char *aspect, *funname, *args; + int prio; + bool success; + +protected: + void doExec (); + +public: + OVGEnterCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *aspect, int prio, + const char *funname, const char *args, + ObjViewFunction *function); + ~OVGEnterCommand (); + + bool calcVisibility (ObjViewGraph *graph); + + char *createMessage (const char *c1, const char *c2, const char *c3a, + const char *c3b); + void freeMessage (char *message); + + char *createName (); + void freeName (char *name); + int getColor (); + ObjViewFunction *getParent (); + bool isSelectable (); + void select (); +}; + +class OVGLeaveCommand: public OVGNavigableCommand +{ +private: + OVGEnterCommand *enterCommand; + char *vals; + bool success; + +protected: + void doExec (); + +public: + OVGLeaveCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *vals, + OVGEnterCommand *enterCommand); + ~OVGLeaveCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + +class OVGAddMessageCommand: public OVGNavigableCommand +{ +private: + char *aspect, *message; + int prio; + +protected: + void doExec (); + +public: + OVGAddMessageCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *aspect, int prio, + const char *message, ObjViewFunction *function); + ~OVGAddMessageCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + + +class OVGAddMarkCommand: public OVGNavigableCommand +{ +private: + char *aspect, *mark; + int prio; + +protected: + void doExec (); + +public: + OVGAddMarkCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *aspect, int prio, + const char *mark, ObjViewFunction *function); + ~OVGAddMarkCommand (); + + bool calcVisibility (ObjViewGraph *graph); + + const char *getMark () { return mark; } + bool isSelectable (); + void select (); +}; + + +class OVGCreateCommand: public OVGNavigableCommand +{ +private: + char *className; + +protected: + void doExec (); + +public: + OVGCreateCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *className, + ObjViewFunction *function); + ~OVGCreateCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + + +class OVGAddAssocCommand: public OVGNavigableCommand +{ +private: + char *id2; + +protected: + void doExec (); + +public: + OVGAddAssocCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id1, const char *id2, + ObjViewFunction *function); + ~OVGAddAssocCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + + +class OVGAddAttrCommand: public OVGNavigableCommand +{ +private: + char *name, *value; + dw::Label *smallLabel; + OVGAttribute *attribute; + int childNo; + +protected: + void doExec (); + void doShow (); + void doHide (); + +public: + OVGAddAttrCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, const char *name, const char *value, + ObjViewFunction *function); + ~OVGAddAttrCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + + +class OVGDeleteCommand: public OVGNavigableCommand +{ +protected: + void doExec (); + +public: + OVGDeleteCommand (const char *fileName, int lineNo, ObjViewGraph *graph, + const char *id, ObjViewFunction *function); + ~OVGDeleteCommand (); + + bool calcVisibility (ObjViewGraph *graph); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_COMMANDS_HH__ diff --git a/objects/objview_controller.cc b/objects/objview_controller.cc new file mode 100644 index 0000000..8d40dbc --- /dev/null +++ b/objects/objview_controller.cc @@ -0,0 +1,171 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include "objview_controller.hh" + +using namespace rtfl::tools; + +namespace rtfl { + +namespace objects { + +void ObjViewController::objMsg (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + graph->addCommand (new OVGAddMessageCommand (info->fileName, info->lineNo, + graph, id, aspect, prio, + message, + graph->getLastEnterCommand ()), + true); +} + +void ObjViewController::objMark (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *message) +{ + OVGAddMarkCommand *markCommand = + new OVGAddMarkCommand (info->fileName, info->lineNo, graph, id, aspect, + prio, message, graph->getLastEnterCommand ()); + graph->addCommand (markCommand, true); + graph->addCommandMark (markCommand); +} + +void ObjViewController::objMsgStart (CommonLineInfo *info, const char *id) +{ + OVGIncIndentCommand *startCommand = + new OVGIncIndentCommand (info->fileName, info->lineNo, graph, id, + graph->getLastEnterCommand ()); + graph->addCommand (startCommand, true); + graph->pushStartCommand (startCommand); +} + +void ObjViewController::objMsgEnd (CommonLineInfo *info, const char *id) +{ + OVGIncIndentCommand *lastStartCommand = graph->getLastStartCommand (); + if (lastStartCommand == NULL) + fprintf (stderr, "'obj-msg-end' without 'obj-msg-start:\n%s\n", + info->completeLine); + else { + graph->addCommand (new OVGDecIndentCommand (info->fileName, info->lineNo, + graph, id, lastStartCommand, + graph->getLastEnterCommand()), + true); + graph->popStartCommand (); + } +} + +void ObjViewController::objEnter (CommonLineInfo *info, const char *id, + const char *aspect, int prio, + const char *funname, const char *args) +{ + OVGEnterCommand *enterCommand = + new OVGEnterCommand (info->fileName, info->lineNo, graph, id, aspect, + prio, funname, args, graph->getLastEnterCommand ()); + graph->addCommand (enterCommand, true); + graph->pushEnterCommand (enterCommand); +} + +void ObjViewController::objLeave (CommonLineInfo *info, const char *id, + const char *vals) +{ + OVGEnterCommand *lastEnterCommand = graph->getLastEnterCommand (); + if (lastEnterCommand == NULL) + fprintf (stderr, "'obj-leave' without 'obj-enter:\n%s\n", + info->completeLine); + else { + graph->addCommand (new OVGLeaveCommand (info->fileName, info->lineNo, + graph, id, vals, + lastEnterCommand), + true); + graph->popEnterCommand (); + } +} + +void ObjViewController::objCreate (CommonLineInfo *info, const char *id, + const char *klass) +{ + graph->addCommand (new OVGCreateCommand (info->fileName, info->lineNo, graph, + id, klass, + graph->getLastEnterCommand ()), + true); +} + +void ObjViewController::objIdent (CommonLineInfo *info, const char *id1, + const char *id2) +{ + // TODO Is this not done by ObjdentController? + graph->addIdentity (id1, id2); +} + +void ObjViewController::objNoIdent (CommonLineInfo *info) +{ +} + +void ObjViewController::objAssoc (CommonLineInfo *info, const char *parent, + const char *child) +{ + graph->addCommand (new OVGAddAssocCommand (info->fileName, info->lineNo, + graph, parent, child, + graph->getLastEnterCommand ()), + true); +} + +void ObjViewController::objSet (CommonLineInfo *info, const char *id, + const char *var, const char *val) +{ + graph->addCommand (new OVGAddAttrCommand (info->fileName, info->lineNo, + graph, id, var, val, + graph->getLastEnterCommand ()), + true); +} + +void ObjViewController::objClassColor (CommonLineInfo *info, const char *klass, + const char *color) +{ + graph->setClassColor (klass, color); +} + +void ObjViewController::objObjectColor (CommonLineInfo *info, const char *id, + const char *color) +{ + graph->setObjectColor (id, color); +} + +void ObjViewController::objDelete (CommonLineInfo *info, const char *id) +{ + graph->addCommand (new OVGDeleteCommand (info->fileName, info->lineNo, graph, + id, graph->getLastEnterCommand ()), + true); +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objview_controller.hh b/objects/objview_controller.hh new file mode 100644 index 0000000..cc14a7f --- /dev/null +++ b/objects/objview_controller.hh @@ -0,0 +1,51 @@ +#ifndef __OBJECTS_OBJVIEW_CONTROLLER_HH__ +#define __OBJECTS_OBJVIEW_CONTROLLER_HH__ + +#include "objects_parser.hh" +#include "objview_graph.hh" + +namespace rtfl { + +namespace objects { + +class ObjViewController: public ObjectsControllerBase +{ +private: + ObjViewGraph *graph; + +public: + ObjViewController (ObjViewGraph *graph) { this->graph = graph; } + + void objMsg (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMark (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *message); + void objMsgStart (tools::CommonLineInfo *info, const char *id); + void objMsgEnd (tools::CommonLineInfo *info, const char *id); + void objEnter (tools::CommonLineInfo *info, const char *id, + const char *aspect, int prio, const char *funname, + const char *args); + void objLeave (tools::CommonLineInfo *info, const char *id, + const char *vals); + void objCreate (tools::CommonLineInfo *info, const char *id, + const char *klass); + void objIdent (tools::CommonLineInfo *info, const char *id1, + const char *id2); + void objNoIdent (tools::CommonLineInfo *info); + void objAssoc (tools::CommonLineInfo *info, const char *parent, + const char *child); + void objSet (tools::CommonLineInfo *info, const char *id, const char *var, + const char *val); + void objClassColor (tools::CommonLineInfo *info, const char *klass, + const char *color); + void objObjectColor (tools::CommonLineInfo *info, const char *id, + const char *color); + void objDelete (tools::CommonLineInfo *info, const char *id); + +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_CONTROLLER_HH__ diff --git a/objects/objview_graph.cc b/objects/objview_graph.cc new file mode 100644 index 0000000..6528f8a --- /dev/null +++ b/objects/objview_graph.cc @@ -0,0 +1,984 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objview_graph.hh" + +#include <fnmatch.h> +#include <unistd.h> + +using namespace lout::object; +using namespace lout::container; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; +using namespace rtfl::dw; + +namespace rtfl { + +namespace objects { + +void ObjViewGraphListener::close (ObjViewStacktraceWindow *window) +{ + graph->stacktraceWindows->removeRef (window); +} + +// ---------------------------------------------------------------------- + +OVGAttributesList::OVGAttributesList (OVGAttributesList *parent) +{ + this->parent = parent; + + childNoCount = numChildrenShown = 0; + childrenShown = new BitSet (8); + shown = true; + + if (parent) + childNo = parent->registerChild (); + else + childNo = -1; + + attributes = new HashTable<String, OVGAttribute> (true, true); +} + +OVGAttributesList::~OVGAttributesList () +{ + if (parent) + parent->unregisterChild (childNo); + + delete childrenShown; + + delete attributes; +} + +void OVGAttributesList::initWidgets (Style *widgetStyle, dw::Box *parent, + bool showLarge) +{ + toggle = new Toggle (showLarge); + toggle->setStyle (widgetStyle); + parent->addChild (toggle); + + vbox = new VBox (false); + vbox->setStyle (widgetStyle); + toggle->setLarge (vbox); +} + +int OVGAttributesList::registerChild () +{ + int childNo = childNoCount++; + + //printf (" %p (%p) / %d -- registerChild => %d\n", + // this, parent, this->childNo, childNo); + + childShown (childNo); + return childNo; +} + +void OVGAttributesList::unregisterChild (int childNo) +{ + // Should (could?) actually destroy this attribute (if this is an + // attribute) when no registered children are left. + childHidden (childNo); +} + +void OVGAttributesList::childShown (int childNo) +{ + if (!childrenShown->get (childNo)) { + childrenShown->set (childNo, true); + numChildrenShown++; + checkVisibility (); + } +} + +void OVGAttributesList::childHidden (int childNo) +{ + if (childrenShown->get (childNo)) { + childrenShown->set (childNo, false); + numChildrenShown--; + checkVisibility (); + } +} + +void OVGAttributesList::checkVisibility () +{ + //printf (" %p (%p) / %d -- %d children shown\n", + // this, parent, childNo, numChildrenShown); + + if (shown) { + if (numChildrenShown <= 0) { + shown = false; + hide (); + if (parent) + parent->childHidden (childNo); + } + } else { + if (numChildrenShown > 0) { + shown = true; + show (); + if (parent) + parent->childShown (childNo); + } + } +} + +// ---------------------------------------------------------------------- + +OVGTopAttributes::OVGTopAttributes () : OVGAttributesList (NULL) +{ + sortedList = new Vector<String> (4, false); +} + +OVGTopAttributes::~OVGTopAttributes () +{ + delete sortedList; +} + +int OVGTopAttributes::add (String *key, OVGAttribute *attribute) +{ + int newPos = sortedList->insertSorted (key); + attributes->put (key, attribute); + return newPos; +} + +void OVGTopAttributes::show () +{ + // Keep the top list visible. +} + +void OVGTopAttributes::hide () +{ + // Keep the top list visible. +} + +// ---------------------------------------------------------------------- + +OVGAttribute::OVGAttribute (const char *name, OVGAttributesList *parent) : + OVGAttributesList (parent) +{ + this->name = strdup (name); +} + +OVGAttribute::~OVGAttribute () +{ + free (name); +} + +void OVGAttribute::initWidgets (Style *widgetStyle, dw::Box *parent, + bool showLarge, int newPos) +{ + hbox = new HBox (false); + hbox->setStyle (widgetStyle); + parent->addChild (hbox, newPos); + + label = new Label (name); + label->setStyle (widgetStyle); + hbox->addChild (label); + + OVGAttributesList::initWidgets (widgetStyle, hbox, showLarge); +} + +int OVGAttribute::add (String *key, OVGAttribute *attribute) +{ + attributes->put (key, attribute); + return -1; +} + +void OVGAttribute::show () +{ + hbox->show (); +} + +void OVGAttribute::hide () +{ + hbox->hide (); +} + +// ---------------------------------------------------------------------- + +ObjViewGraph::GraphObject::GraphObject (ObjViewGraph *graph, const char *id) +{ + this->graph = graph; + this->id = strdup (id); + + className = NULL; + node = NULL; + attributes = NULL; + messageStyle = graph->noBorderStyle; + messageStyle->ref (); +} + +ObjViewGraph::GraphObject::~GraphObject () +{ + // Simplified, when the whole graph is deleted. + if (node && !graph->inDestructor) + delete node; + + free (id); + if (className) + free (className); + if (attributes) + delete attributes; + if (messageStyle) + messageStyle->unref (); +} + +// ---------------------------------------------------------------------- + +int ObjViewGraph::ColorComparator::specifity (const char *pattern) +{ + int s = 0; + for (const char *p = pattern; *p; p++) { + if (*p == '*') + s -= 100; + else if (*p == '?') + s += 1; + else + s += 100; + } + return s; +} + +int ObjViewGraph::ColorComparator::compare (Object *o1, Object *o2) +{ + return specifity (((Color*)o2)->identifier) + - specifity (((Color*)o1)->identifier); +} + +// ---------------------------------------------------------------------- + +ObjViewGraph::Color::Color (const char *identifier, const char *color, + Layout *layout) +{ + this->identifier = strdup (identifier); + + this->color = NULL; + if (*color == '#') { + char *end; + int c = (int)strtol (color + 1, &end, 16); + if (*end == 0) { + if (end - color == 7) + this->color = style::Color::create (layout, c); + else if (end - color == 4) + this->color = + style::Color::create (layout, + (c & 0xf00) >> 8 | (c && 0xf0) >> 4 + | (c & 0xf)); + this->color->ref (); + } + } + + if (this->color == NULL) + fprintf (stderr, "WARNING: invalid color '%s'.\n", color); +} + +ObjViewGraph::Color::~Color () +{ + free (identifier); + + if (this->color) + this->color->unref (); +} + +// ---------------------------------------------------------------------- + +ObjViewGraph::ObjViewGraph (ObjViewFilterTool *filterTool) +{ + inDestructor = false; + + this->filterTool = filterTool; + + commands = new Vector<OVGCommand> (4, true); + navigableCommands = new Vector<OVGCommand> (4, false); + + objectsById = new HashTable<String, GraphObject> (true, false); + allObjects = new Vector<GraphObject> (1, true); + classColors = new Vector<Color> (1, true); + objectColors = new Vector<Color> (1, true); + enterCommands = new Stack<OVGEnterCommand> (false); + startCommands = new Stack<OVGIncIndentCommand> (false); + + navigableCommandsPos = hiddenBefore = hiddenAfter = -1; + numVisibleCommands = 0; + objectContents = objectMessages = true; + codeViewer = strdup ("xterm -e 'vi +%n %p' &"); + + stacktraceWindows = new List <ObjViewStacktraceWindow> (true); + stacktraceListener = new ObjViewGraphListener (this); + + nodeStyle = noBorderStyle = topBorderStyle = bottomBorderStyle = + leftBorderStyle = NULL; +} + +ObjViewGraph::~ObjViewGraph () +{ + inDestructor = true; + + free (codeViewer); + delete commands; + delete navigableCommands; + + delete objectsById; + delete allObjects; + delete classColors; + delete objectColors; + delete enterCommands; + delete startCommands; + + delete stacktraceWindows; + delete stacktraceListener; + + if (nodeStyle) + nodeStyle->unref (); + if (noBorderStyle) + noBorderStyle->unref (); + if (topBorderStyle) + topBorderStyle->unref (); + if (bottomBorderStyle) + bottomBorderStyle->unref (); + if (leftBorderStyle) + leftBorderStyle->unref (); +} + +void ObjViewGraph::initStyles (const char *fontName, int fontSize, int fgColor, + int graphBgColor, int objBgColor, + int borderThickness, int graphMargin) +{ + StyleAttrs styleAttrs; + styleAttrs.initValues (); + + FontAttrs fontAttrs; + fontAttrs.name = fontName; + fontAttrs.size = fontSize; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + + styleAttrs.padding.setVal (graphMargin); + styleAttrs.color = style::Color::create (layout, fgColor); + styleAttrs.setBorderColor (styleAttrs.color); + styleAttrs.setBorderStyle (BORDER_SOLID); + styleAttrs.backgroundColor = style::Color::create (layout, graphBgColor); + Style *graphStyle = Style::create (&styleAttrs); + setStyle (graphStyle); + + styleAttrs.padding.setVal (0); + styleAttrs.backgroundColor = style::Color::create (layout, objBgColor); + styleAttrs.borderWidth.setVal (1); + nodeStyle = Style::create (&styleAttrs); + setRefStyle (nodeStyle); + + styleAttrs.backgroundColor = NULL; + styleAttrs.borderWidth.setVal (0); + noBorderStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.borderWidth.top = 1; + topBorderStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.borderWidth.bottom = 1; + bottomBorderStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.borderWidth.left = 1; + leftBorderStyle = Style::create (&styleAttrs); +} + +ObjViewGraph::GraphObject *ObjViewGraph::ensureObject (const char *id) +{ + String key (id); + if (!objectsById->contains (&key)) { + GraphObject *obj = new GraphObject (this, id); + + obj->node = new Toggle (objectContents); + applyClassOrObjectStyle (obj); + addNode (obj->node); + + obj->id1 = new Label (id); + obj->id1->setStyle (noBorderStyle); + obj->node->setSmall (obj->id1); + + VBox *large = new VBox (false); + large->setStyle (leftBorderStyle); + obj->node->setLarge (large); + + obj->id2 = new Label (id); + obj->id2->setStyle (bottomBorderStyle); + large->addChild (obj->id2); + + obj->attributes = new OVGTopAttributes (); + obj->attributes->initWidgets (noBorderStyle, large, true); + + Toggle *mToggle = new Toggle (objectMessages); + mToggle->setStyle (topBorderStyle); + large->addChild (mToggle); + + obj->messages = new VBox (false); + obj->messages->setStyle (noBorderStyle); + mToggle->setLarge (obj->messages); + + objectsById->put (new String (id), obj); + allObjects->put (obj); + } + + return objectsById->get (&key); +} + +void ObjViewGraph::addMessage (const char *id, const char *message, + HBox **mainBox, Label **indexLabel, + Label **mainLabel) +{ + GraphObject *obj = ensureObject (id); + + HBox *hbox = new HBox (false); + hbox->setStyle (noBorderStyle); + obj->messages->addChild (hbox); + + Label *label1 = new Label ("???: "); + label1->setStyle (obj->messageStyle); + hbox->addChild (label1); + + Label *label2 = new Label (message); + label2->setStyle (noBorderStyle); + hbox->addChild (label2); + + if (mainBox) + *mainBox = hbox; + if (indexLabel) + *indexLabel = label1; + if (mainLabel) + *mainLabel = label2; +} + +bool ObjViewGraph::incIndent (const char *id) +{ + GraphObject *obj = ensureObject (id); + + Style *oldMessageStyle = obj->messageStyle; + StyleAttrs styleAttrs = *(obj->messageStyle); + styleAttrs.padding.left += 3 * obj->messageStyle->font->spaceWidth; + obj->messageStyle = Style::create (&styleAttrs); + oldMessageStyle->unref (); + + return true; // just for symmetry with decIndent +} + +bool ObjViewGraph::decIndent (const char *id) +{ + GraphObject *obj = ensureObject (id); + + Style *oldMessageStyle = obj->messageStyle; + int indentWidth = 3 * obj->messageStyle->font->spaceWidth; + if (oldMessageStyle->padding.left >= indentWidth) { + StyleAttrs styleAttrs = *(obj->messageStyle); + styleAttrs.padding.left -= indentWidth; + obj->messageStyle = Style::create (&styleAttrs); + oldMessageStyle->unref (); + return true; + } else + return false; +} + +OVGAttribute *ObjViewGraph::addAttribute (const char *id, const char *name, + const char *value, Label **smallLabel, + HBox **histBox, Label **indexLabel, + Label **histLabel) +{ + GraphObject *obj = ensureObject (id); + + int numDots = 0; + for (const char *s = name; *s; s++) + if (*s == '.') + numDots++; + + //printf ("'%s' contains %d dots\n", name, numDots); + char **parts = new char*[numDots + 2]; + + int i = 0; + const char *s = name; + for (const char *e = s; i < numDots + 1; e++) + if (*e == '.') { + parts[i] = strndup (s, e - s); + //printf ("%d of %d: '%s'\n", i, numDots + 1, parts[i]); + s = e + 1; + i++; + } else if (*e == 0) { + parts[i] = strdup (s); + //printf ("%d of %d (last): '%s'\n", i, numDots + 1, parts[i]); + s = e; + i++; + } + + //printf ("finished\n"); + + parts[numDots + 1] = NULL; + + OVGAttribute *attribute = addAttribute (obj->attributes, parts, value, + smallLabel, histBox, indexLabel, + histLabel); + + for (char **p = parts; *p; p++) + free (*p); + + delete[] parts; + + return attribute; +} + +// Returns "large" label. +OVGAttribute *ObjViewGraph::addAttribute (OVGAttributesList *attributesList, + char **parts, const char *value, + Label **smallLabel, HBox **histBox, + Label **indexLabel, Label **histLabel) +{ + if (*parts) { + String key (*parts); + OVGAttribute *attribute = attributesList->get (&key); + if (attribute == NULL) { + String *str = new String (*parts); + attribute = new OVGAttribute (*parts, attributesList); + int newPos = attributesList->add (str, attribute); + attribute->initWidgets (noBorderStyle, attributesList->vbox, false, + newPos); + } + + OVGAttribute *subAttribute = addAttribute (attribute, parts + 1, value, + smallLabel, histBox, + indexLabel, histLabel); + return subAttribute ? subAttribute : attribute; + } else { + HBox *hbox = new HBox (false); + hbox->setStyle (noBorderStyle); + attributesList->vbox->addChild (hbox); + + Label *label1 = new Label ("???: "); + label1->setStyle (noBorderStyle); + hbox->addChild (label1); + + Label *label2 = new Label (value); + label2->setStyle (noBorderStyle); + hbox->addChild (label2); + + if (attributesList->toggle->getSmall () != NULL) { + assert (attributesList->toggle->getSmall() + ->instanceOf (Label::CLASS_ID)); + ((Label*)attributesList->toggle->getSmall())->setText (value); + } else { + Label *cur = new Label (value); + cur->setStyle (noBorderStyle); + attributesList->toggle->setSmall (cur); + } + + if (smallLabel) + *smallLabel = (Label*)attributesList ->toggle->getSmall (); + if (histBox) + *histBox = hbox; + if (indexLabel) + *indexLabel = label1; + if (histLabel) + *histLabel = label2; + + // Here, no attribute is created; the caller must discard this + // return value. + return NULL; + } +} + +void ObjViewGraph::setClassName (const char *id, const char *className) +{ + GraphObject *obj = ensureObject (id); + + if (obj->className) + free (obj->className); + obj->className = strdup (className); + + int bufLen = strlen (id) + 2 + strlen (className) + 1; + char *buf = new char[bufLen]; + snprintf (buf, bufLen, "%s: %s", id, className); + + obj->id1->setText (buf); + obj->id2->setText (buf); + + delete[] buf; + + applyClassOrObjectStyle (obj); +} + +style::Color *ObjViewGraph::getObjectColor (GraphObject *obj) +{ + style::Color *objectColor = NULL; + + // TODO Identities are currently not considered + for (int i = 0; objectColor == NULL && i < objectColors->size (); i++) { + Color *color = objectColors->get (i); + if (color->color && strcmp (color->identifier, obj->id) == 0) + objectColor = color->color; + } + + if (obj->className) { + for (int i = 0; objectColor == NULL && i < classColors->size (); i++) { + Color *color = classColors->get (i); + if (color->color && + fnmatch (color->identifier, obj->className, 0) == 0) + objectColor = color->color; + } + } + + return objectColor; +} + +void ObjViewGraph::applyClassOrObjectStyle (GraphObject *obj) +{ + style::Color *usedColor = getObjectColor (obj); + + if (usedColor) { + // TODO Perhaps it is better to create styles initially (and + // change them when setStyle is called?). Should especially + // reduce memory usage. + StyleAttrs attrs = *nodeStyle; + attrs.backgroundColor = usedColor; + Style *newStyle = Style::create (&attrs); + obj->node->setStyle (newStyle); + newStyle->unref (); + } else + obj->node->setStyle (nodeStyle); +} + + +void ObjViewGraph::applyClassOrObjectStyles () +{ + for (typed::Iterator<String> it = objectsById->iterator (); + it.hasNext (); ) { + String *key = it.getNext (); + GraphObject *obj = (GraphObject*) objectsById->get(key); + applyClassOrObjectStyle (obj); + } +} + +int ObjViewGraph::getObjectColor (const char *id) +{ + String key (id); + GraphObject *obj = (GraphObject*) objectsById->get (&key); + if (obj) { + style::Color *objectColor = getObjectColor (obj); + return objectColor ? + objectColor->getColor () : nodeStyle->backgroundColor->getColor (); + } else + return nodeStyle->backgroundColor->getColor (); +} + +void ObjViewGraph::addIdentity (const char *id1, const char *id2) +{ + // Done in ObjIdentController. +} + +void ObjViewGraph::addAssoc (const char *id1, const char *id2) +{ + GraphObject *obj1 = ensureObject (id1); + GraphObject *obj2 = ensureObject (id2); + addEdge (obj1->node, obj2->node); +} + +void ObjViewGraph::setClassColor (const char *klass, const char *color) +{ + ColorComparator cmp; + classColors->insertSorted (new Color (klass, color, layout), &cmp); + applyClassOrObjectStyles (); +} + +void ObjViewGraph::setObjectColor (const char *id, const char *color) +{ + objectColors->put (new Color (id, color, layout)); + applyClassOrObjectStyles (); +} + +void ObjViewGraph::addCommand (OVGCommand *command, bool navigable) +{ + commands->put (command); + command->exec (this); + + if (navigable) { + navigableCommands->put (command); + command->setNavigableCommandsIndex (this, navigableCommands->size () -1); + + if (command->calcVisibility (this)) { + command->show (this); + command->setVisibleIndex (this, numVisibleCommands); + numVisibleCommands++; + } else + command->hide (this); + } +} + +void ObjViewGraph::clearSelection () +{ + if (navigableCommandsPos != -1) + navigableCommands->get(navigableCommandsPos)->unselect (this); +} + +void ObjViewGraph::unclearSelection () +{ + if (navigableCommandsPos != - 1) { + navigableCommands->get(navigableCommandsPos)->select (this); + navigableCommands->get(navigableCommandsPos)->scrollTo (this); + } +} + +int ObjViewGraph::firstCommand () +{ + if (hiddenBefore != -1) + return hiddenBefore; + else + return navigableCommands->size () > 0 ? 0 : -1; +} + +int ObjViewGraph::lastCommand () +{ + if (hiddenAfter != -1) + return hiddenAfter; + else + return navigableCommands->size () - 1; // may be -1 +} + +void ObjViewGraph::previousCommand () +{ + clearSelection (); + + if (numVisibleCommands == 0) + // No visible command; + navigableCommandsPos = -1; + else + do { + if (navigableCommandsPos == -1) + navigableCommandsPos = lastCommand (); + else { + navigableCommandsPos--; + if (navigableCommandsPos < firstCommand ()) + navigableCommandsPos = lastCommand (); + } + } while (!navigableCommands->get(navigableCommandsPos)->isVisible (this)); + + unclearSelection (); +} + +void ObjViewGraph::nextCommand () +{ + clearSelection (); + + if (numVisibleCommands == 0) + // No visible command; + navigableCommandsPos = -1; + else { + do { + if (navigableCommandsPos == -1) + navigableCommandsPos = firstCommand (); + else { + navigableCommandsPos++; + if (navigableCommandsPos > lastCommand ()) + navigableCommandsPos = firstCommand (); + } + } while (!navigableCommands->get(navigableCommandsPos)->isVisible (this)); + } + + unclearSelection (); +} + +void ObjViewGraph::viewCodeOfCommand () +{ + if (navigableCommandsPos != -1) { + OVGCommand *command = navigableCommands->get (navigableCommandsPos); + + char lineNoBuf[32]; + char partBuf[32] = { 0, 0 }; + StringBuffer sb; + + snprintf (lineNoBuf, 32, "+%d", command->getLineNo ()); + + for (char *s = codeViewer; *s; s++) { + if (*s == '%' && (s[1] == 'p' || s[1] == 'n')) { + s++; + switch (*s) { + case 'p': + sb.append (command->getFileName ()); + break; + + case 'n': + sb.append (lineNoBuf); + break; + } + } else { + *partBuf = *s; + sb.append (partBuf); + } + } + + system (sb.getChars()); + } +} + +void ObjViewGraph::showStackTraceOfCommand () +{ + if (navigableCommandsPos != -1) { + OVGCommand *command = navigableCommands->get (navigableCommandsPos); + ObjViewStacktraceWindow *stWin = + new ObjViewStacktraceWindow (command->getFunction (), + stacktraceListener); + stacktraceWindows->append (stWin); + stWin->show (); + stWin->update (); + } +} + +void ObjViewGraph::switchBetweenRelatedCommands () +{ + if (navigableCommandsPos != -1) { + OVGCommand *command = navigableCommands->get (navigableCommandsPos); + OVGCommand *relatedCommand = command->getRelatedCommand (); + if (relatedCommand && relatedCommand->isVisible (this)) { + clearSelection (); + navigableCommandsPos = + relatedCommand->getNavigableCommandsIndex (this); + unclearSelection (); + } + } +} + +void ObjViewGraph::setCommand (int index) +{ + clearSelection (); + navigableCommandsPos = index; + unclearSelection (); +} + +void ObjViewGraph::hideBeforeCommand () +{ + // Note: The selected command cannot be hidden, so there are never + // elements to show again. + + if (navigableCommandsPos != -1) { + hiddenBefore = navigableCommandsPos; + for (int i = 0; i < hiddenBefore; i++) + navigableCommands->get(i)->hide (this); + } + + recalculateCommandsVisibility (); // Could be faster +} + +void ObjViewGraph::hideAfterCommand () +{ + // See also note in hideBeforeCommand(). + + if (navigableCommandsPos != -1) { + hiddenAfter = navigableCommandsPos; + for (int i = hiddenAfter + 1; i < navigableCommands->size (); i++) + navigableCommands->get(i)->hide (this); + } + + recalculateCommandsVisibility (); // Could be faster +} + +void ObjViewGraph::hideAllCommands () +{ + navigableCommandsPos = -1; + hiddenBefore = navigableCommands->size (); + for (int i = 0; i < navigableCommands->size (); i++) + navigableCommands->get(i)->hide (this); + + recalculateCommandsVisibility (); // Necessary? +} + +void ObjViewGraph::showBeforeCommand () +{ + if (hiddenBefore != -1) { + hiddenBefore = -1; + recalculateCommandsVisibility (); // Could be faster + } +} + +void ObjViewGraph::showAfterCommand () +{ + if (hiddenAfter != -1) { + hiddenAfter = -1; + recalculateCommandsVisibility (); // Could be faster + } +} + +void ObjViewGraph::showAllCommands () +{ + if (hiddenBefore != -1 || hiddenAfter != -1) { + hiddenBefore = hiddenAfter = -1; + recalculateCommandsVisibility (); // Could be faster + } +} + +void ObjViewGraph::setCodeViewer (const char *codeViewer) +{ + free (this->codeViewer); + this->codeViewer = strdup (codeViewer); +} + +void ObjViewGraph::recalculateCommandsVisibility () +{ + numVisibleCommands = 0; + for (int i = max (firstCommand (), 0); i <= lastCommand (); i++) { + OVGCommand *command = navigableCommands->get (i); + + if (command->calcVisibility (this)) { + command->show (this); + command->setVisibleIndex (this, numVisibleCommands); + numVisibleCommands++; + } else + command->hide (this); + } + + if (navigableCommandsPos != -1) { + if (!navigableCommands->get(navigableCommandsPos)->isVisible (this)) + // Selected command has become invisible. Select next one. (May + // also be the first one, if, e. g., the selected one was the last + // one. Could test this and instead select the last visible + // *before*.) + nextCommand (); + else + // May have become out of view. + navigableCommands->get(navigableCommandsPos)->scrollTo (this); + } + + // Update stacktrace windows (hidden commands should not be selectable). + for (lout::container::typed::Iterator <ObjViewStacktraceWindow> it = + stacktraceWindows->iterator (); it.hasNext (); ) { + ObjViewStacktraceWindow *stWin = it.getNext (); + stWin->update (); + } +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objview_graph.hh b/objects/objview_graph.hh new file mode 100644 index 0000000..9619f7e --- /dev/null +++ b/objects/objview_graph.hh @@ -0,0 +1,305 @@ +#ifndef __OBJECTS_OBJVIEW_GRAPH_HH__ +#define __OBJECTS_OBJVIEW_GRAPH_HH__ + +#include "objview_stacktrace.hh" +#include "objview_commands.hh" +#include "dwr/graph.hh" +#include "dwr/graph2.hh" +#include "dwr/toggle.hh" +#include "dwr/vbox.hh" +#include "dwr/hbox.hh" +#include "dwr/label.hh" +#include "common/tools.hh" + +namespace rtfl { + +namespace objects { + +enum OVGCommandType { + OVG_COMMAND_CREATE, + OVG_COMMAND_INDENT, + OVG_COMMAND_MESSAGE, + OVG_COMMAND_MARK, + OVG_COMMAND_FUNCTION, + OVG_COMMAND_ASSOC, + OVG_COMMAND_ADD_ATTR, + OVG_COMMAND_DELETE +}; + +class ObjViewGraphListener: public ObjViewListener +{ +private: + ObjViewGraph *graph; + +public: + ObjViewGraphListener (ObjViewGraph *graph) { this->graph = graph; } + + void close (ObjViewStacktraceWindow *window); +}; + + +class ObjViewFilterTool +{ +public: + virtual void addAspect (const char *aspect) = 0; + virtual void addPriority (int priority) = 0; + virtual bool isAspectSelected (const char *aspect) = 0; + virtual bool isPrioritySelected (int priority) = 0; + virtual bool isTypeSelected (OVGCommandType type) = 0; + virtual void addMark (OVGAddMarkCommand *markCommand) = 0; +}; + +class OVGAttribute; + +class OVGAttributesList: public lout::object::Object +{ +private: + OVGAttributesList *parent; + int childNoCount, numChildrenShown, childNo; + lout::misc::BitSet *childrenShown; + bool shown; + + void checkVisibility (); + +protected: + lout::container::typed::HashTable<lout::object::String, + OVGAttribute> *attributes; + + virtual void show () = 0; + virtual void hide () = 0; + +public: + dw::VBox *vbox; + dw::Toggle *toggle; + + OVGAttributesList (OVGAttributesList *parent); + ~OVGAttributesList (); + + inline OVGAttribute *get (lout::object::String *key) + { return attributes->get (key); } + virtual int add (lout::object::String *key, OVGAttribute *attribute) = 0; + + void initWidgets (::dw::core::style::Style *widgetStyle, dw::Box *parent, + bool showLarge); + + int registerChild (); + void unregisterChild (int childNo); + void childShown (int childNo); + void childHidden (int childNo); +}; + +class OVGTopAttributes: public OVGAttributesList +{ +protected: + lout::container::typed::Vector<lout::object::String> *sortedList; + + void show (); + void hide (); + +public: + OVGTopAttributes (); + ~OVGTopAttributes (); + + int add (lout::object::String *key, OVGAttribute *attribute); +}; + +// Hint: the OVGAttributesList'ness refers to the children. +class OVGAttribute: public OVGAttributesList +{ +protected: + void show (); + void hide (); + +public: + dw::HBox *hbox; + dw::Label *label; + char *name; + + OVGAttribute (const char *name, OVGAttributesList *parent); + ~OVGAttribute (); + + void initWidgets (::dw::core::style::Style *widgetStyle, + dw::Box *parent, bool showLarge, int newPos); + + int add (lout::object::String *key, OVGAttribute *attribute); +}; + +class ObjViewGraph: public +#if USE_GRAPH2 + dw::Graph2 +#else + dw::Graph +#endif +{ + friend class OVGCommonCommand; + friend class OVGIncIndentCommand; + friend class OVGDecIndentCommand; + friend class OVGEnterCommand; + friend class OVGLeaveCommand; + friend class OVGAddMessageCommand; + friend class OVGAddMarkCommand; + friend class OVGCreateCommand; + friend class OVGAddAssocCommand; + friend class OVGAddAttrCommand; + friend class OVGDeleteCommand; + friend class ObjViewGraphListener; + +private: + class CreateRefCommand; + + class GraphObject: public lout::object::Object + { + friend class CreateRefCommand; + friend class ObjViewGraph; + + private: + ObjViewGraph *graph; + + public: + char *id, *className; + ::dw::core::style::Style *messageStyle; + dw::Toggle *node; + dw::Label *id1, *id2; + OVGTopAttributes *attributes; + dw::VBox *messages; + + GraphObject (ObjViewGraph *graph, const char *id); + ~GraphObject (); + + }; + + // Inernally added for the first occurence of an identity. + class CreateRefCommand: public OVGCommonCommand + { + protected: + void doExec (); + void doUndo (); + + public: + CreateRefCommand (ObjViewGraph *graph, const char *id, + GraphObject *graphObject); + }; + + class Color: public lout::object::Object + { + public: + char *identifier; + ::dw::core::style::Color *color; + + Color (const char *classPattern, const char *color, + ::dw::core::Layout *layout); + ~Color (); + }; + + class ColorComparator: public lout::object::Comparator + { + private: + int specifity (const char *pattern); + + public: + int compare(Object *o1, Object *o2); + }; + + ObjViewFilterTool *filterTool; + + lout::container::typed::Vector<OVGCommand> *commands; + lout::container::typed::Vector<OVGCommand> *navigableCommands; + lout::container::typed::HashTable<lout::object::String, + GraphObject> *objectsById; + lout::container::typed::Vector<GraphObject> *allObjects; + lout::container::typed::Vector<Color> *classColors; + lout::container::typed::Vector<Color> *objectColors; + + lout::container::typed::Stack<OVGEnterCommand> *enterCommands; + lout::container::typed::Stack<OVGIncIndentCommand> *startCommands; + + bool objectContents, objectMessages; + char *codeViewer; + int navigableCommandsPos, hiddenBefore, hiddenAfter, numVisibleCommands; + + ::dw::core::style::Style *nodeStyle, *noBorderStyle, *topBorderStyle, + *bottomBorderStyle, *leftBorderStyle; + bool inDestructor; + + lout::container::typed::List <ObjViewStacktraceWindow> *stacktraceWindows; + ObjViewGraphListener *stacktraceListener; + + GraphObject *ensureObject (const char *id); + OVGAttribute *addAttribute (OVGAttributesList *attributesList, char **parts, + const char *value, dw::Label **smallLabel, + dw::HBox **histBox, dw::Label **indexLabel, + dw::Label **histLabel); + + ::dw::core::style::Color *getObjectColor (GraphObject *obj); + void applyClassOrObjectStyle (GraphObject *obj); + void applyClassOrObjectStyles (); + + void addMessage (const char *id, const char *message, dw::HBox **mainBox, + dw::Label **indexLabel, dw::Label **mainLabel); + bool incIndent (const char *id); + bool decIndent (const char *id); + OVGAttribute *addAttribute (const char *id, const char *name, + const char *value, dw::Label **smallLabel, + dw::HBox **histBox, dw::Label **indexLabel, + dw::Label **histLabel); + void setClassName (const char *id, const char *className); + void addAssoc (const char *id1, const char *id2); + + void clearSelection (); + void unclearSelection (); + int firstCommand (); + int lastCommand (); + +public: + ObjViewGraph (ObjViewFilterTool *objectFilterTool); + ~ObjViewGraph (); + void initStyles (const char *fontName, int fontSize, int fgColor, + int graphBgColor, int objBgColor, int borderThickness, + int grraphMargin); + + void addCommand (OVGCommand *command, bool navigable); + void addIdentity (const char *id1, const char *id2); + void setClassColor (const char *klass, const char *color); + void setObjectColor (const char *id, const char *color); + + int getObjectColor (const char *id); + + inline void pushEnterCommand (OVGEnterCommand *enterCommand) + { enterCommands->push (enterCommand); } + void popEnterCommand () { enterCommands->pop (); } + OVGEnterCommand *getLastEnterCommand () { return enterCommands->getTop (); } + + inline void pushStartCommand (OVGIncIndentCommand *startCommand) + { startCommands->push (startCommand); } + void popStartCommand () { startCommands->pop (); } + OVGIncIndentCommand *getLastStartCommand () + { return startCommands->getTop (); } + + inline void addCommandMark (OVGAddMarkCommand *markCommand) + { filterTool->addMark (markCommand); } + + void previousCommand (); + void nextCommand (); + void viewCodeOfCommand (); + void showStackTraceOfCommand (); + void switchBetweenRelatedCommands (); + void setCommand (int index); + void hideBeforeCommand (); + void hideAfterCommand (); + void hideAllCommands (); + void showBeforeCommand (); + void showAfterCommand (); + void showAllCommands (); + + inline void showObjectContents (bool val) { objectContents = val; } + inline void showObjectMessages (bool val) { objectMessages = val; } + void setCodeViewer (const char *codeViewer); + + void recalculateCommandsVisibility (); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_GRAPH_HH__ diff --git a/objects/objview_stacktrace.cc b/objects/objview_stacktrace.cc new file mode 100644 index 0000000..e5c8dc8 --- /dev/null +++ b/objects/objview_stacktrace.cc @@ -0,0 +1,127 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objview_stacktrace.hh" + +#include "config.h" + +#include <FL/Fl_Button.H> +#include <FL/Fl_Return_Button.H> + +namespace rtfl { + +namespace objects { + +ObjViewStacktraceWindow::ObjViewStacktraceWindow (ObjViewFunction *topFunction, + ObjViewListener *listener) : + Fl_Window (WIDTH, HEIGHT, "RTFL: Stack trace") +{ + callback(windowCallback, this); + + browser = + new Fl_Hold_Browser (SPACE, SPACE, WIDTH - 2 * SPACE, + HEIGHT - 3 * SPACE - BUTTON_HEIGHT, NULL); + Fl_Button *jumpTo = + new Fl_Button(WIDTH - 2 * BUTTON_WIDTH - 2 * SPACE, + HEIGHT - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH, + BUTTON_HEIGHT, "Jump to"); + jumpTo->callback (ObjViewStacktraceWindow::jumpTo, this); + Fl_Return_Button *close = + new Fl_Return_Button(WIDTH - BUTTON_WIDTH - SPACE, + HEIGHT - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH, + BUTTON_HEIGHT, "Close"); + close->callback (ObjViewStacktraceWindow::close, this); + + resizable (browser); + + this->topFunction = topFunction; + this->listener = listener; +} + +ObjViewStacktraceWindow::~ObjViewStacktraceWindow () +{ +} + +void ObjViewStacktraceWindow::update () +{ + browser->clear (); + + for (ObjViewFunction *fun = topFunction; fun; + fun = fun->getParent ()) { + char *name = fun->createName (); + + char name2[1024]; + name2[0] = 0; + + if (!fun->isSelectable ()) { + strcpy (name2, "@i"); + } + + sprintf (name2 + strlen (name2), "@B%ld@.", + ((long)fun->getColor ()) << 8); + + char *s = name2 + strlen (name2); + for (int i = 0; name[i]; i++) { + if (name[i] == '@') + *(s++) = '@'; + *(s++) = name[i]; + } + *s = 0; + + browser->add (name2, NULL); + + fun->freeName (name); + } +} + +void ObjViewStacktraceWindow::windowCallback (Fl_Widget *widget, void *data) +{ + close (widget, data); +} + +void ObjViewStacktraceWindow::jumpTo (Fl_Widget *widget, void *data) +{ + ObjViewStacktraceWindow *win = (ObjViewStacktraceWindow*)data; + + ObjViewFunction *fun = win->topFunction; + for (int i = 1; fun; fun = fun->getParent (), i++) { + if (win->browser->selected (i) && fun->isSelectable ()) + fun->select (); + } +} + +void ObjViewStacktraceWindow::close (Fl_Widget *widget, void *data) +{ + ObjViewStacktraceWindow *win = (ObjViewStacktraceWindow*)data; + win->listener->close (win); +} + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objview_stacktrace.hh b/objects/objview_stacktrace.hh new file mode 100644 index 0000000..5ea8d7e --- /dev/null +++ b/objects/objview_stacktrace.hh @@ -0,0 +1,60 @@ +#ifndef __OBJECTS_OBJVIEW_STRACKTRACE_HH__ +#define __OBJECTS_OBJVIEW_STRACKTRACE_HH__ + +#include <FL/Fl_Window.H> +#include <FL/Fl_Hold_Browser.H> + +#include "lout/object.hh" + +namespace rtfl { + +namespace objects { + +class ObjViewFunction +{ +public: + virtual char *createName () = 0; + virtual void freeName (char *name) = 0; + virtual int getColor () = 0; // as 0xRRGGBB + virtual ObjViewFunction *getParent () = 0; + virtual bool isSelectable () = 0; + virtual void select () = 0; +}; + +class ObjViewStacktraceWindow; + +// Somewhat simpler than using signals +class ObjViewListener +{ +public: + virtual ~ObjViewListener () { } + virtual void close (ObjViewStacktraceWindow *window) = 0; +}; + +class ObjViewStacktraceWindow: public Fl_Window, public lout::object::Object +{ +private: + enum { WIDTH = 450, HEIGHT = 300, BUTTON_WIDTH = 80, BUTTON_HEIGHT = 25, + SPACE = 10 }; + + Fl_Hold_Browser *browser; + ObjViewFunction *topFunction; + ObjViewListener *listener; + + static void windowCallback (Fl_Widget *widget, void *data); + static void jumpTo (Fl_Widget *widget, void *data); + static void close (Fl_Widget *widget, void *data); + +public: + ObjViewStacktraceWindow (ObjViewFunction *topFunction, + ObjViewListener *listener); + ~ObjViewStacktraceWindow (); + + void update (); +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_STRACKTRACE_HH__ diff --git a/objects/objview_window.cc b/objects/objview_window.cc new file mode 100644 index 0000000..211076d --- /dev/null +++ b/objects/objview_window.cc @@ -0,0 +1,707 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl.H> +#include <limits.h> + +#include "objview_window.hh" + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" + +#include "dwr/graph.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; + +namespace rtfl { + +namespace objects { + +ObjViewWindow::Aspect::Aspect (ObjViewWindow *window, const char *name, + bool set) +{ + this->window = window; + this->name = strdup (name); + this->set = set; +} + + +ObjViewWindow::Aspect::~Aspect () +{ + free (name); +} + + +bool ObjViewWindow::Aspect::equals(Object *other) +{ + Aspect *otherAspect = (Aspect*)other; + return strcmp (name, otherAspect->name) == 0 && + ((set && otherAspect->set) || (!set && !otherAspect->set)); +} + + +int ObjViewWindow::Aspect::compareTo(Comparable *other) +{ + Aspect *otherAspect = (Aspect*)other; + return strcmp (name, otherAspect->name); +} + + +// ---------------------------------------------------------------------- + + +ObjViewWindow::Priority::Priority (ObjViewWindow *window, int value) +{ + this->window = window; + this->value = value; +} + + +bool ObjViewWindow::Priority::equals(Object *other) +{ + Priority *otherPriority = (Priority*)other; + return value == otherPriority->value; +} + + +int ObjViewWindow::Priority::compareTo(Comparable *other) +{ + Priority *otherPriority = (Priority*)other; + return value - otherPriority->value; +} + + +// ---------------------------------------------------------------------- + + +ObjViewWindow::ObjViewWindow (int width, int height, const char *title): + Fl_Window (width, height, title) +{ + shown = false; + + aspects = new HashTable<String, Aspect> (true, true); + aspectsMenuPositions = new Vector<Integer> (1, true);; + aspectsInitiallySet = true; + + priorities = new HashTable<Integer, Priority> (true, true); + prioritiesMenuPositions = new Vector<Integer> (1, true);; + selectedPriority = INT_MAX; + + // Special priority "no priority" (INT_MAX): + Priority *noLimitsPriority = new Priority (this, INT_MAX); + priorities->put (new Integer (INT_MAX), noLimitsPriority); + + aboutWindow = NULL; + + int menuHeight = 24; + + FltkPlatform *platform = new FltkPlatform (); + layout = new Layout (platform); + + callback(windowCallback, NULL); + box(FL_NO_BOX); + + menu = new Fl_Menu_Bar(0, 0, width, menuHeight); + + FltkViewport *viewport = + new FltkViewport (0, menuHeight, width, height - menuHeight); + layout->attachView (viewport); + + graph = new ObjViewGraph (this); + layout->setWidget (graph); + graph->initStyles ("DejaVu Sans", 12, 0x000000, 0xffffff, 0xffffd0, 1, 10); + + Fl_Menu_Item menuItems[] = { + { "&File", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "Quit", FL_COMMAND + 'q', quit, this, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Command", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "&Previous", FL_COMMAND + 'p', previous, this, 0, 0, 0, 0, 0 }, + { "&Next", FL_COMMAND + 'n', next, this, 0, 0, 0, 0, 0 }, + { "View &Code", FL_COMMAND + 'c', viewCode, this, FL_MENU_DIVIDER, + 0, 0, 0, 0 }, + { "Hide &before", FL_COMMAND + 'b', hideBefore, this, 0, 0, 0, 0, 0 }, + { "Hide &after", FL_COMMAND + 'a', hideAfter, this, 0, 0, 0, 0, 0 }, + { "&Hide all", FL_COMMAND + 'h', hideAll, this, FL_MENU_DIVIDER, + 0, 0, 0, 0 }, + { "Show b&efore", FL_COMMAND + 'e', showBefore, this, 0, 0, 0, 0, 0 }, + { "Show a&fter", FL_COMMAND + 'f', showAfter, this, 0, 0, 0, 0, 0 }, + { "&Show all", FL_COMMAND + 's', showAll, this, FL_MENU_DIVIDER, + 0, 0, 0, 0 }, + { "Show stack trace", FL_COMMAND + 't', showStackTrace, this, 0, 0, 0, 0, + 0 }, + { "Switch between related", FL_COMMAND + 'r', switchBetweenRelated, this, + FL_MENU_DIVIDER, 0, 0, 0, 0 }, + { "&Creations", 0, toggleCommandTypeVisibility, this, FL_MENU_TOGGLE, + 0, 0, 0, 0 }, + { "&Indentations", 0, toggleCommandTypeVisibility, this, FL_MENU_TOGGLE, + 0, 0, 0, 0 }, + { "&Messages", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { "M&arks", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { "&Functions", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { "A&ssociations", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { "A&ttributes", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { "&Deletions", 0, toggleCommandTypeVisibility, this, + FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Aspects", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "&Show all", 0, showAllAspects, this, FL_MENU_TOGGLE | FL_MENU_VALUE, + 0, 0, 0, 0 }, + { "&Hide all", 0, hideAllAspects, this, FL_MENU_TOGGLE | FL_MENU_DIVIDER, + 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Priorities", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "&No limit", 0, setPriority, noLimitsPriority, + FL_MENU_RADIO | FL_MENU_VALUE /*| FL_MENU_DIVIDER*/, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Marks", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { "&Help", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "&About RTFL", 0, about, this, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + menu->copy(menuItems); + + noLimitsPriority->menuItem = + (Fl_Menu_Item*)(menu->find_item ("&Priorities/&No limit")); + + resizable(viewport); +} + +ObjViewWindow::~ObjViewWindow () +{ + delete aspects; + delete aspectsMenuPositions; + delete priorities; + delete prioritiesMenuPositions; + if (aboutWindow) + delete aboutWindow; + delete layout; +} + + +void ObjViewWindow::show () +{ + shown = true; + + Integer key (selectedPriority); + Priority *priorityEntry = priorities->get (&key); + priorityEntry->menuItem->activate (); + + Fl_Window::show (); +} + +void ObjViewWindow::addAspect (const char *aspect) +{ + addAspect (aspect, aspectsInitiallySet); +} + +void ObjViewWindow::setAspectsInitiallySet (bool val) +{ + aspectsInitiallySet = val; + showOrHideAllAspects (aspectsInitiallySet); + + if (aspectsInitiallySet) { + getShowAllAspectsMenuItem()->set (); + getHideAllAspectsMenuItem()->clear (); + } else { + getShowAllAspectsMenuItem()->clear (); + getHideAllAspectsMenuItem()->set (); + } +} + +void ObjViewWindow::addPriority (int priority) +{ + addPriority (priority, false); +} + + +bool ObjViewWindow::isAspectSelected (const char *aspect) +{ + String key (aspect); + Aspect *aspectEntry = aspects->get (&key); + assert (aspectEntry != NULL); // Should have been added before. + return aspectEntry->set; +} + + +bool ObjViewWindow::isPrioritySelected (int priority) +{ + return priority <= selectedPriority; +} + + +bool ObjViewWindow::isTypeSelected (OVGCommandType type) +{ + switch (type) { + case OVG_COMMAND_CREATE: + return getCreateMenuItem()->value (); + + case OVG_COMMAND_INDENT: + return getIndentMenuItem()->value (); + + case OVG_COMMAND_MESSAGE: + return getMessageMenuItem()->value (); + + case OVG_COMMAND_MARK: + return getMarkMenuItem()->value (); + + case OVG_COMMAND_FUNCTION: + return getFunctionMenuItem()->value (); + + case OVG_COMMAND_ASSOC: + return getAssocMenuItem()->value (); + + case OVG_COMMAND_ADD_ATTR: + return getAddAttrMenuItem()->value (); + + case OVG_COMMAND_DELETE: + return getDeleteMenuItem()->value (); + + } + + assertNotReached (); + return false; +} + +void ObjViewWindow::addMark (OVGAddMarkCommand *markCommand) +{ + int pathLen = 6 + strlen (markCommand->getMark ()) + 1; + char *path = new char[pathLen]; + snprintf (path, pathLen, "Marks/%s", markCommand->getMark ()); + int menuPos = menu->add (path, 0, NULL, NULL, 0); + delete[] path; + Fl_Menu_Item *menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos); + menuItem->callback (jumpToMark, markCommand); +} + + +void ObjViewWindow::addAspect (const char *aspect, bool val) +{ + String key (aspect); + Aspect *aspectEntry = aspects->get (&key); + if (aspectEntry == NULL) { + aspectEntry = new Aspect (this, aspect, val); + aspects->put (new String (aspect), aspectEntry); + +#if 1 + int pathLen = 8 + strlen (aspect) + 1; + char *path = new char[pathLen]; + snprintf (path, pathLen, "Aspects/%s", aspect); + int menuPos = + menu->add (path, 0, NULL, NULL, + FL_MENU_TOGGLE | (aspectEntry->set ? FL_MENU_VALUE : 0)); + delete[] path; + aspectEntry->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos); + aspectEntry->menuItem->callback (toggleAspect, aspectEntry); + + // TODO aspectsMenuPositions is only used in the deactivated + // code. +#else + // Aspects are sorted alphabetically, so we choose the following + // approach: + // + // 1. Add a new entry, which is not used for the new aspect, but + // for the last aspect after sorting. For this reason, a + // random name is choosen. + // + // 2. Sort all aspects. + // + // 3. Re-assign all aspects to the menu entries. + + // 1. Add new entry. + // + // It seams that a menu entry must have a unique name, so it is + // important to choose a temporary one which does not collide + // with an aspect. This one is the result of: + // + // $ head -c 32 < /dev/random | base64 | sed 's/\///g' + // + // (Further checks are not done, hoping that noone chooses this + // as an aspect.) + + int menuPos = + menu->add ("Aspects/xgIUCOm3HTvK5P33Dsk3lPSL1qgXg4Iye3APe0ckMo0=", + 0, NULL, NULL, FL_MENU_TOGGLE); + aspectsMenuPositions->put (new Integer (menuPos)); + + // 2. Sort all aspects. + + Vector<Aspect> sortedAspects (1, false); + for (lout::container::typed::Iterator<String> it = aspects->iterator (); + it.hasNext (); ) { + String *key = it.getNext (); + Aspect *value = aspects->get (key); + sortedAspects.insertSorted (value); + } + + // 3. Re-assign all aspects to the menu entries. + + assert (sortedAspects.size () == aspectsMenuPositions->size ()); + + for (int i = 0; i < sortedAspects.size (); i++) { + Aspect *aspect = sortedAspects.get (i); + int menuPos = aspectsMenuPositions->get(i)->getValue (); + + // This is somewhat ugly (ignoring "const"): + aspect->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos); + //aspect->menuItem->label (aspect->name); + menu->replace (menuPos, aspect->name); + if (aspect->set) + aspect->menuItem->set (); + else + aspect->menuItem->clear (); + aspect->menuItem->callback (toggleAspect, aspect); + } +#endif + } +} + + +ObjViewWindow::Priority *ObjViewWindow::addPriority (int priority, bool val) +{ + // Similar to addAspect(). + + Integer key (priority); + Priority *priorityEntry = priorities->get (&key); + if (priorityEntry == NULL) { + priorityEntry = new Priority (this, priority); + priorities->put (new Integer (priority), priorityEntry); + +#if 1 + int pathLen = 11 + 5 + 1; + char path[pathLen]; + snprintf (path, pathLen, "Priorities/%d", priority); + int menuPos = menu->add (path, 0, NULL, NULL, + FL_MENU_RADIO | (val ? FL_MENU_VALUE : 0)); + priorityEntry->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos); + priorityEntry->menuItem->callback (setPriority, priorityEntry); + + // TODO prioritiesMenuPositions is only used in the deactivated + // code. +#else + // 1. Add new entry. + + int menuPos = + menu->add ("Priorities/xgIUCOm3HTvK5P33Dsk3lPSL1qgXg4Iye3APe0ckMo0=", + 0, NULL, NULL, FL_MENU_RADIO); + prioritiesMenuPositions->put (new Integer (menuPos)); + + // 2. Sort all priorities. + + Vector<Priority> sortedPriorities (1, false); + for (lout::container::typed::Iterator<Integer> it = + priorities->iterator (); it.hasNext (); ) { + Integer *key = it.getNext (); + Priority *value = priorities->get (key); + // "No limits" entry is not sorted. + if (value->value != INT_MAX) + sortedPriorities.insertSorted (value); + } + + // 3. Re-assign all priorities to the menu entries (except "No limits"). + + assert (sortedPriorities.size () == prioritiesMenuPositions->size ()); + + for (int i = 0; i < sortedPriorities.size (); i++) { + Priority *priority = sortedPriorities.get (i); + int menuPos = prioritiesMenuPositions->get(i)->getValue (); + + // Assuming maximal priorities of 99999 (snprintf will at + // least prevent buffer overflows). + snprintf (priority->valueBuf, 6, "%d", priority->value); + + // This is somewhat ugly (ignoring "const"): + priority->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos); + //priority->menuItem->label (priority->valueBuf); + menu->replace (menuPos, priority->valueBuf); + if (priority->value == selectedPriority) + priority->menuItem->set (); + else + priority->menuItem->clear (); + priority->menuItem->callback (setPriority, priority); + } +#endif + } + + return priorityEntry; +} + +void ObjViewWindow::setPriority (int priority) +{ + assert (!shown); + + selectedPriority = priority; + addPriority (priority, true); + graph->recalculateCommandsVisibility (); +} + + +void ObjViewWindow::setAnyPriority () +{ + assert (!shown); + + selectedPriority = INT_MAX; + graph->recalculateCommandsVisibility (); +} + + + +void ObjViewWindow::quit (Fl_Widget *widget, void *data) +{ + exit (0); +} + + +void ObjViewWindow::previous (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->previousCommand (); +} + + +void ObjViewWindow::next (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->nextCommand (); +} + + +void ObjViewWindow::viewCode (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->viewCodeOfCommand (); +} + + +void ObjViewWindow::hideBefore (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->hideBeforeCommand (); +} + + +void ObjViewWindow::hideAfter (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->hideAfterCommand (); +} + + +void ObjViewWindow::hideAll (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->hideAllCommands (); +} + + +void ObjViewWindow::showBefore (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->showBeforeCommand (); +} + + +void ObjViewWindow::showAfter (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->showAfterCommand (); +} + + +void ObjViewWindow::showAll (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->showAllCommands (); +} + +void ObjViewWindow::showStackTrace (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->showStackTraceOfCommand (); +} + +void ObjViewWindow::switchBetweenRelated (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->switchBetweenRelatedCommands (); +} + +void ObjViewWindow::toggleCommandTypeVisibility (Fl_Widget *widget, void *data) +{ + ((ObjViewWindow*)data)->graph->recalculateCommandsVisibility (); +} + + +void ObjViewWindow::showAllAspects (Fl_Widget *widget, void *data) +{ + ObjViewWindow *objectsWindow = (ObjViewWindow*)data; + bool showSet = objectsWindow->getShowAllAspectsMenuItem()->value (); + objectsWindow->showOrHideAllAspects (showSet); + + if (showSet) + objectsWindow->getHideAllAspectsMenuItem()->clear (); + else + objectsWindow->getHideAllAspectsMenuItem()->set (); + + objectsWindow->aspectsInitiallySet = true; +} + + +void ObjViewWindow::hideAllAspects (Fl_Widget *widget, void *data) +{ + ObjViewWindow *objectsWindow = (ObjViewWindow*)data; + bool hideSet = objectsWindow->getHideAllAspectsMenuItem()->value (); + objectsWindow->showOrHideAllAspects (!hideSet); + + if (hideSet) + objectsWindow->getShowAllAspectsMenuItem()->clear (); + else + objectsWindow->getShowAllAspectsMenuItem()->set (); + + objectsWindow->aspectsInitiallySet = false; +} + + +void ObjViewWindow::showOrHideAllAspects (bool value) +{ + for (lout::container::typed::Iterator<String> it = aspects->iterator (); + it.hasNext (); ) { + String *key = it.getNext (); + Aspect *aspect = aspects->get (key); + aspect->set = value; + if (value) + aspect->menuItem->set (); + else + aspect->menuItem->clear (); + } + + graph->recalculateCommandsVisibility (); +} + + +void ObjViewWindow::toggleAspect (Fl_Widget *widget, void *data) +{ + Aspect *aspect = (Aspect*)data; + aspect->set = aspect->menuItem->value (); + + bool allShown = true, allHidden = true; + for (lout::container::typed::Iterator<String> it = + aspect->window->aspects->iterator (); it.hasNext (); ) { + String *key = it.getNext (); + Aspect *value = aspect->window->aspects->get (key); + if (value->set) + allHidden = false; + else + allShown = false; + } + + // There is at least one entry; otherwise, this method would not be + // called. (With no elements, allShown && allHidden would always be + // true.) + assert (!(allShown && allHidden)); + + // If neither all shown, nor all hidden, aspectsInitiallySet is not + // changed. + + if (allShown) { + aspect->window->getShowAllAspectsMenuItem()->set (); + aspect->window->aspectsInitiallySet = true; + } else + aspect->window->getShowAllAspectsMenuItem()->clear (); + + if (allHidden) { + aspect->window->getHideAllAspectsMenuItem()->set (); + aspect->window->aspectsInitiallySet = false; + } else + aspect->window->getHideAllAspectsMenuItem()->clear (); + + aspect->window->graph->recalculateCommandsVisibility (); +} + + +void ObjViewWindow::setPriority (Fl_Widget *widget, void *data) +{ + Priority *priority = (Priority*)data; + priority->window->selectedPriority = priority->value; + priority->window->graph->recalculateCommandsVisibility (); +} + +void ObjViewWindow::jumpToMark (Fl_Widget *widget, void *data) +{ + OVGAddMarkCommand *markCommand = (OVGAddMarkCommand*)data; + if (markCommand->isSelectable ()) + markCommand->select (); +} + +void ObjViewWindow::about (Fl_Widget *widget, void *data) +{ + ObjViewWindow *window = (ObjViewWindow*)data; + + if (window->aboutWindow == NULL) + window->aboutWindow = + new common::AboutWindow("rtfl-objview", + "; with the following exception:\n" + "\n" + "The copyright holders of RTFL give you " + "permission to link rtfl-objview " + "statically or dynamically against all " + "versions of the graphviz library, which are " + "published by AT&T Corp. under one of the " + "following licenses:\n" + "\n" + "- Common Public License version 1.0 as " + "published by International Business Machines " + " Corporation (IBM), or\n" + "- Eclipse Public License version 1.0 as " + "published by the Eclipse Foundation", + common::AboutWindow::HEIGHT_EXCEPTION); + window->aboutWindow->show (); +} + + +void ObjViewWindow::windowCallback (Fl_Widget *widget, void *data) +{ + // Ignore escape key. TODO Looks rather hackish to me. + if (Fl::event_key() != FL_Escape) + quit (widget, data); +} + + +} // namespace objects + +} // namespace rtfl diff --git a/objects/objview_window.hh b/objects/objview_window.hh new file mode 100644 index 0000000..053b845 --- /dev/null +++ b/objects/objview_window.hh @@ -0,0 +1,189 @@ +#ifndef __OBJECTS_OBJVIEW_WINDOW_HH__ +#define __OBJECTS_OBJVIEW_WINDOW_HH__ + +#include <FL/Fl_Window.H> +#include <FL/Fl_Menu_Bar.H> +#include <FL/Fl_Menu_Item.H> +#include "objview_graph.hh" +#include "lout/container.hh" +#include "common/about.hh" + +namespace rtfl { + +namespace objects { + +class ObjViewWindow: public Fl_Window, public ObjViewFilterTool +{ +private: + class Aspect: public lout::object::Comparable + { + public: + ObjViewWindow *window; + char *name; + bool set; + Fl_Menu_Item *menuItem; + + Aspect (ObjViewWindow *window, const char *name, bool set); + ~Aspect (); + + bool equals(Object *other); + int compareTo(Comparable *other); + }; + + class Priority: public lout::object::Comparable + { + public: + ObjViewWindow *window; + int value; + char valueBuf[6]; + Fl_Menu_Item *menuItem; + + Priority (ObjViewWindow *window, int value); + + bool equals(Object *other); + int compareTo(Comparable *other); + }; + + bool shown; + + ::dw::core::Layout *layout; + ObjViewGraph *graph; + Fl_Window *aboutWindow; + Fl_Menu_Bar *menu; + + lout::container::typed::HashTable<lout::object::String, Aspect> *aspects; + lout::container::typed::Vector<lout::object::Integer> *aspectsMenuPositions; + bool aspectsInitiallySet; + + lout::container::typed::HashTable<lout::object::Integer, Priority> + *priorities; + lout::container::typed::Vector<lout::object::Integer> + *prioritiesMenuPositions; + int selectedPriority; + + inline Fl_Menu_Item *getCreateMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/&Creations"); } + + inline Fl_Menu_Item *getIndentMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/&Indentations"); } + + inline Fl_Menu_Item *getMessageMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/&Messages"); } + + inline Fl_Menu_Item *getMarkMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/M&arks"); } + + inline Fl_Menu_Item *getFunctionMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/&Functions"); } + + inline Fl_Menu_Item *getAssocMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/A&ssociations"); } + + inline Fl_Menu_Item *getAddAttrMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/A&ttributes"); } + + inline Fl_Menu_Item *getDeleteMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Command/&Deletions"); } + + inline Fl_Menu_Item *getShowAllAspectsMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Aspects/&Show all"); } + + inline Fl_Menu_Item *getHideAllAspectsMenuItem () + { return (Fl_Menu_Item*)menu->find_item("&Aspects/&Hide all"); } + + static void quit (Fl_Widget *widget, void *data); + static void previous (Fl_Widget *widget, void *data); + static void next (Fl_Widget *widget, void *data); + static void viewCode (Fl_Widget *widget, void *data); + static void hideBefore (Fl_Widget *widget, void *data); + static void hideAfter (Fl_Widget *widget, void *data); + static void hideAll (Fl_Widget *widget, void *data); + static void showBefore (Fl_Widget *widget, void *data); + static void showAfter (Fl_Widget *widget, void *data); + static void showAll (Fl_Widget *widget, void *data); + static void showStackTrace (Fl_Widget *widget, void *data); + static void switchBetweenRelated (Fl_Widget *widget, void *data); + static void toggleCommandTypeVisibility (Fl_Widget *widget, void *data); + static void showAllAspects (Fl_Widget *widget, void *data); + static void hideAllAspects (Fl_Widget *widget, void *data); + static void toggleAspect (Fl_Widget *widget, void *data); + static void setPriority (Fl_Widget *widget, void *data); + static void jumpToMark (Fl_Widget *widget, void *data); + static void about (Fl_Widget *widget, void *data); + static void windowCallback (Fl_Widget *widget, void *data); + + Priority *addPriority (int priority, bool val); + + void showOrHideAllAspects (bool value); + +public: + ObjViewWindow (int width, int height, const char *title); + ~ObjViewWindow (); + + void show(); + + void addAspect (const char *aspect); + void addPriority (int priority); + bool isAspectSelected (const char *aspect); + bool isPrioritySelected (int priority); + bool isTypeSelected (OVGCommandType type); + void addMark (OVGAddMarkCommand *markCommand); + + void addAspect (const char *aspect, bool val); + void setAspectsInitiallySet (bool val); + void setPriority (int priority); + void setAnyPriority (); + + inline ObjViewGraph *getObjViewGraph () { return graph; } + + inline void showCreateCommands (bool val) { + if (val) getCreateMenuItem()->set (); + else getCreateMenuItem()->clear (); + } + + inline void showIndentCommands (bool val) { + if (val) getIndentMenuItem()->set (); + else getIndentMenuItem()->clear (); + } + + inline void showMessageCommands (bool val) { + if (val) getMessageMenuItem()->set (); + else getMessageMenuItem()->clear (); + } + + inline void showMarkCommands (bool val) { + if (val) getMarkMenuItem()->set (); + else getMarkMenuItem()->clear (); + } + + inline void showFunctionCommands (bool val) { + if (val) getFunctionMenuItem()->set (); + else getFunctionMenuItem()->clear (); + } + + inline void showAssocCommands (bool val) { + if (val) getAssocMenuItem()->set (); + else getAssocMenuItem()->clear (); + } + + inline void showAddAttrCommands (bool val) { + if (val) getAddAttrMenuItem()->set (); + else getAddAttrMenuItem()->clear (); + } + + inline void showDeleteCommands (bool val) { + if (val) getDeleteMenuItem()->set (); + else getDeleteMenuItem()->clear (); + } + + inline void showObjectMessages (bool val) { graph->showObjectMessages(val); } + inline void showObjectContents (bool val) { graph->showObjectContents(val); } + inline void setCodeViewer (const char *codeViewer) { + graph->setCodeViewer (codeViewer); } +}; + +} // namespace objects + +} // namespace rtfl + +#endif // __OBJECTS_OBJVIEW_WINDOW_HH__ diff --git a/objects/rtfl_objbase.cc b/objects/rtfl_objbase.cc new file mode 100644 index 0000000..1da24bb --- /dev/null +++ b/objects/rtfl_objbase.cc @@ -0,0 +1,45 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "objects_parser.hh" +#include "objects_writer.hh" +#include "objdelete_controller.hh" +#include "objident_controller.hh" + +#include <fcntl.h> + +using namespace rtfl::tools; +using namespace rtfl::objects; + +int main(int argc, char **argv) +{ + LinesSourceSequence source (true); + int fd = open (".rtfl", O_RDONLY); + if (fd != -1) + source.add (new BlockingLinesSource (fd)); + source.add (new BlockingLinesSource (0)); + + ObjectsWriter writer; + ObjIdentController identController (&writer); + ObjDeleteController deleteController (&identController); + ObjectsParser parser (&deleteController); + source.setup (&parser); + + return 0; +} diff --git a/objects/rtfl_objcount.cc b/objects/rtfl_objcount.cc new file mode 100644 index 0000000..fb71fbb --- /dev/null +++ b/objects/rtfl_objcount.cc @@ -0,0 +1,40 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "common/fltk_lines.hh" +#include "objcount_window.hh" +#include "objcount_controller.hh" + +using namespace rtfl::objects; +using namespace rtfl::common; + +int main(int argc, char **argv) +{ + ObjCountWindow *window = new ObjCountWindow(800, 600, "RTFL: Objects count"); + window->show(); + + FltkDefaultSource source; + ObjCountController controller (window->getTable ()); + ObjectsParser parser (&controller); + source.setup (&parser); + + int errorCode = Fl::run(); + delete window; + return errorCode; +} diff --git a/objects/rtfl_objview.cc b/objects/rtfl_objview.cc new file mode 100644 index 0000000..bed41bd --- /dev/null +++ b/objects/rtfl_objview.cc @@ -0,0 +1,218 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <FL/Fl.H> +#include "common/fltk_lines.hh" +#include "objview_window.hh" +#include "objview_controller.hh" +#include "objdelete_controller.hh" +#include "objident_controller.hh" + +using namespace rtfl::objects; +using namespace rtfl::common; + +static void printHelp (const char *argv0) +{ + fprintf + (stderr, "Usage: %s <options>\n" + "\n" + "Options:\n" + " -a <aspect> Show,\n" + " -A <aspect> hide aspects. <aspect> may be '*'.\n" + " -b Do,\n" + " -B do not apply \".rtfl\" and filtering identities " + "(what \n" + " \"rtfl-objbase\" does).\n" + " -m Show,\n" + " -M hide the messages of all object boxes.\n" + " -o Show,\n" + " -O hide the contents of all object boxes.\n" + " -p <prio> Set priority. <prio> is a number or '*'.\n" + " -t <types> Show,\n" + " -T <types> hide command types. <types> is a sequence of any " + "of the\n" + " characters 'c', 'i', 'm', 'a', 'f', 's', 't', " + "'d'.\n" + " -v <viewer> Use <viewer> to view code. Contains '%%p' as " + "variable for\n" + " the path, and '%%n' for the line number.\n" + "\n" + "See RTFL documentation for more details.\n", + argv0); +} + +static bool toggleCommandTypes (ObjViewWindow *window, const char *arg, + bool val) +{ + for (const char *s = arg; *s; s++) { + switch (*s) { + case 'c': + window->showCreateCommands (val); + break; + + case 'i': + window->showIndentCommands (val); + break; + + case 'm': + window->showMessageCommands (val); + break; + + case 'a': + window->showMarkCommands (val); + break; + + case 'f': + window->showFunctionCommands (val); + break; + + case 's': + window->showAssocCommands (val); + break; + + case 't': + window->showAddAttrCommands (val); + break; + + case 'd': + window->showDeleteCommands (val); + break; + + default: + return false; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + ObjViewWindow *window = new ObjViewWindow(800, 600, "RTFL: Objects view"); + + int opt; + bool baseFiltering = true; + + while ((opt = getopt(argc, argv, "a:A:bBMmOop:t:T:v:")) != -1) { + switch (opt) { + case 'a': + if (strcmp (optarg, "*") == 0) + window->setAspectsInitiallySet (true); + else + window->addAspect (optarg, true); + break; + + case 'A': + if (strcmp (optarg, "*") == 0) + window->setAspectsInitiallySet (false); + else + window->addAspect (optarg, false); + break; + + case 'b': + baseFiltering = true; + break; + + case 'B': + baseFiltering = false; + break; + + case 'm': + window->showObjectMessages (true); + break; + + case 'M': + window->showObjectMessages (false); + break; + + case 'o': + window->showObjectContents (true); + break; + + case 'O': + window->showObjectContents (false); + break; + + case 'p': + if (strcmp (optarg, "*") == 0) + window->setAnyPriority (); + else + window->setPriority (atoi (optarg)); + break; + + case 't': + if (!toggleCommandTypes (window, optarg, true)) { + printHelp (argv[0]); + delete window; + return 1; + } + break; + + case 'T': + if (!toggleCommandTypes (window, optarg, false)) { + printHelp (argv[0]); + delete window; + return 1; + } + break; + + case 'v': + window->setCodeViewer (optarg); + break; + + default: + printHelp (argv[0]); + delete window; + return 1; + } + } + + int errorCode; + ObjViewController viewController (window->getObjViewGraph ()); + + if (baseFiltering) { + FltkDefaultSource source; + ObjIdentController identController (&viewController); + ObjDeleteController deleteController (&identController); + ObjectsParser parser (&deleteController); + source.setup (&parser); + window->show(); + errorCode = Fl::run(); + } else { + FltkLinesSource source; + ObjectsParser parser (&viewController); + source.setup (&parser); + window->show(); + errorCode = Fl::run(); + } + + delete window; + return errorCode; +} diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 0000000..6929095 --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,6 @@ +dist_bin_SCRIPTS = \ + rtfl-check-objects \ + rtfl-filter-out-classes \ + rtfl-objfilter \ + rtfl-objtail \ + rtfl-stacktraces diff --git a/scripts/rtfl-check-objects b/scripts/rtfl-check-objects new file mode 100644 index 0000000..0e29e82 --- /dev/null +++ b/scripts/rtfl-check-objects @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +# RTFL +# +# Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Usage: rtfl-check-objects +# +# Use RTFL messages to check for invalid object access. +# +# N. b. that parsing is incorrect, see <doc/rtfl.html#scripts>. + +%exist_objects = { }; +%all_objects = { }; +%ident_objects = { }; + +sub check_object +{ + my $id1 = $_[0], $id2 = $ident_objects{$_[0]}; + if (!($exist_objects{$id1} || ($id2 && $exist_objects{$id2}))) { + if ($all_objects{$id1} || ($id2 && $all_objects{$id2})) { + print "--- Object $id1 has been deleted: ---\n$_"; + } else { + print "--- Object $id1 has never existed: ---\n$_"; + } + } +} + +while(<STDIN>) { + if (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?create:([^:]*):/) { + $exist_objects{$2}++; + $all_objects{$2} = 1; + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?delete:(.*)$/) { + $exist_objects{$2}--; + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?ident:([^:]*):(.*)$/) { + if($2 ne $3) { + $ident_objects{$2} = $3; + $ident_objects{$3} = $2; + } + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?(msg|set|enter|leave):([^:]*):/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?(msg-start|msg-end|leave):(.*)$/) { + check_object ($3); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?assoc:([^:]*):(.*)$/) { + check_object ($2); + check_object ($3); + } +} diff --git a/scripts/rtfl-filter-out-classes b/scripts/rtfl-filter-out-classes new file mode 100644 index 0000000..5c0d2f9 --- /dev/null +++ b/scripts/rtfl-filter-out-classes @@ -0,0 +1,63 @@ +#!/usr/bin/perl + +# RTFL +# +# Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Usage: rtfl-filter-out-classes [<classes> ...] +# +# Filter out all RTFL messages referring to objects belonging to a +# specified set of classes. Each command line argument is a concrete +# class or a (filename) pattern. The latter is useful to exclude whole +# namespaces ("path::to::namespace::*"). +# +# N. b. that parsing is slightly incorrect; escaping is (except partly +# for classes) not considered. + +use File::FnMatch qw(:fnmatch); + +%removed_objects = { }; + +open PIPE, "rtfl-objbase |"; + +while(<PIPE>) { + if (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?create:([^:]*):(.*)$/) { + $removed = 0; + $o = $2; + $c1 = $3; + $c1 =~ s/\\:/:/g; + foreach $c2 (@ARGV) { + if (fnmatch ($c2, $c1)) { + $removed_objects{$o} = 1; + $removed = 1; + } + } + if (!$removed) { print; } + } elsif (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?(msg|set|enter|leave):([^:]*):/ && + $removed_objects{$3}) { + # Suppress. + } elsif (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?(msg-(start|end)|delete|leave):(.*)$/ && + $removed_objects{$4}) { + # Suppress. + } elsif (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?assoc:([^:]*):(.*)$/ && + ($removed_objects{$2} || $removed_objects{$3})) { + # Suppress. + } else { + print; + } +} + +close PIPE; diff --git a/scripts/rtfl-objfilter b/scripts/rtfl-objfilter new file mode 100755 index 0000000..2947e21 --- /dev/null +++ b/scripts/rtfl-objfilter @@ -0,0 +1,110 @@ +#!/usr/bin/perl + +# RTFL +# +# Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Usage: rtfl-objfilter [options] +# +# Filters a stream of RTFL messages by types, aspects and priorities. +# The options -a, -A, -t, -T, and -p are supported and work in the +# same way as for rtfl-objview. By default, nothing is filtered. +# +# N. b. that parsing is incorrect, see <doc/rtfl.html#scripts>. + +$LARGE_INT = 1000000000; + +sub helpAndExit { + die "Syntax: $0 uses same arguments -a, -A, -t, -T, and -p as rtfl-objview."; +} + +sub filter1 { + return $shownTypes{$_[0]}; +} + +sub filter2 { + return $shownTypes{$_[0]} && + (($defaultShow && !$hiddenAspects{$_[1]}) || + (!$defaultShow && !$shownAspects{$_[1]})) && + $_[2] >= $prio; +} + + +%shownAspects = {}; +%hiddenAspects = {}; +$defaultShow = 1; +%shownTypes = + ( "i" => 1, "m" => 1, "a" => 1, "f" => 1, "s" => 1, "t" => 1, "d" => 1 ); +$prio = $LARGE_INT; + +for ($i = 0; $i < scalar @ARGV; $i++) { + $opt = $ARGV[$i]; + helpAndExit if ($i == scalar @ARGV -1); + $arg = $ARGV[++$i]; + + if ($opt eq "-a") { + if (arg eq "*") { $defaultShow = 1; } + else { $shownAspects{$arg} = 1; } + } elsif ($opt eq "-A") { + if (arg eq "*") { $defaultShow = 0; } + else { $hiddenAspects{$arg} = 1; } + } elsif ($opt eq "-t" || $opt eq "-T") { + $show = $opt eq "-t"; + for ($i = 0; $i < length ($arg); $i++) { + $shownTypes{substr ($arg, $i, 1)} = $show; + } + } elsif ($opt eq "-p") { + if ($arg eq "*") { $prio = $LARGE_INT; } + else { $prio = $arg; } + } else { + helpAndExit if ($method eq ""); + } +} + +@shownFuns = (); + +while(<STDIN>) { + if (/^\[rtfl[^\]]*\]/) { + if (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?mark:[^:]*:([^:]*):([^:]*):/) { + print if (filter2 ("a", $2, $3)); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?msg:[^:]*:([^:]*):([^:]*):/) { + print if (filter2 ("m", $2, $3)); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?msg-start:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?msg-end:/) { + print if (filter1 ("i")); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?set:/) { + print if (filter1 ("t")); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?delete:/) { + print if (filter1 ("d")); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?enter:[^:]*:([^:]*):([^:]*):/) { + $shown = filter2 ("f", $2, $3); + print if ($shown); + push @shownFuns, $shown; + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?leave:/) { + $show = pop @shownFuns; + print if ($shown); + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?create:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?assoc:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?color:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?class-color:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?object-color:/ || + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?ident:/) { + print; + } else { + print STDERR "Invalid line: $_"; + } + } +} diff --git a/scripts/rtfl-objtail b/scripts/rtfl-objtail new file mode 100644 index 0000000..d0ec9c0 --- /dev/null +++ b/scripts/rtfl-objtail @@ -0,0 +1,134 @@ +#!/usr/bin/perl + +# RTFL +# +# Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Usage: rtfl-objtail [-a <attributes> ...] [-A <attributes> ...] <len> +# +# Print only the last lines of a stream of RTFL messages, but include +# those RTFL messages which are necessary to understand the last ones, +# like "obj-create", "obj-assoc" etc., when they refer to objects in +# the last lines. +# +# Any number of options "-a" and "-A" may be added with fnmatch(3) +# like patterns; for those attributes specified by "-a", but not +# excluded by "-A", the last attribute definitions before the actual +# tail is shown for relevant objects. +# +# Example: "-a 'foo.*' -A '*.bar'" will show attribute values for +# 'foo.qix', but not for, say, 'foo.bar', since "-A '*.bar'" overrides +# "-a 'foo.*'". +# +# The opposite, "rtfl-objhead", is not needed; simply use "head" to +# get the first lines. +# +# N. b. that parsing is slightly incorrect; escaping is not considered. + +use File::FnMatch qw(:fnmatch); + +sub helpAndExit { + die "Syntax: $0 [-a <attributes> ...] [-A <attributes> ...] <number of lines>"; +} + +$tlen = ""; +@attrs = (); +@neg_attrs = (); + +for ($i = 0; $i < scalar @ARGV; $i++) { + if ($ARGV[$i] eq "-a") { + helpAndExit if ($i == scalar @ARGV -1); + push @attrs, $ARGV[++$i]; + } elsif ($ARGV[$i] eq "-A") { + helpAndExit if ($i == scalar @ARGV -1); + push @neg_attrs, $ARGV[++$i]; + } else { + $tlen = $ARGV[$i]; + } +} + +helpAndExit if ($tlen eq ""); + +@all_lines = (); +%rel_objects = { }; +%last_attrs = { }; + +open PIPE, "rtfl-objbase |"; + +while(<PIPE>) { + if (/^\[rtfl-obj-1.[0-9]+]/) { push @all_lines, $_; } +} + +close PIPE; + +$len = scalar (@all_lines); +if ($tlen > $len) { + $tlen = $len; +} + +# Determine relevant objects from the last lines. +for ($i = $len - $tlen; $i < $len; $i++) { + $_ = $all_lines[$i]; + if (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?(create|msg|set|enter|leave):([^:]*):/ || + /^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?(msg-start|msg-end|delete|leave):(.*)$/) { + $rel_objects{$3} = 1; + } elsif (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?assoc:([^:]*):(.*)$/) { + $rel_objects{$2} = 1; + $rel_objects{$3} = 1; + } +} + +# Determine the last attribute values before the last lines. +for ($i = 0; $i < $len - $tlen; $i++) { + $_ = $all_lines[$i]; + if (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?set:([^:]*):([^:]*):(.*)/ && + $rel_objects{$2}) { + $found = 0; + for ($j = 0; $j < scalar (@attrs) && !$found; $j++) { + if (fnmatch ($attrs[i], $3)) { $found = 1; } + } + for ($j = 0; $j < scalar (@neg_attrs) && $found; $j++) { + if (fnmatch ($neg_attrs[i], $3)) { $found = 0; } + } + + if ($found) { + $last_attrs{"$2:$3"} = $_; + } + } +} + +foreach (keys %last_attrs) { + print $last_attrs{$_}; +} + +# Print all relevant lines (both before the last lines and the last lines). +for ($i = 0; $i < $len; $i++) { + $_ = $all_lines[$i]; + if (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?color:/ || + /^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?class-color:/ || + /^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?object-color:/) { + print; + } elsif ($i >= $len - $tlen) { + print; + } elsif ((/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?create:([^:]*):/ || + /^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?delete:(.*)$/) && + $rel_objects{$2}) { + print; + } elsif (/^\[rtfl-obj-1.[0-9]+][^:]*:[^:]*:[^:]*:(obj-)?assoc:([^:]*):(.*)$/ + && $rel_objects{$2} && $rel_objects{$3}) { + print; + } +} diff --git a/scripts/rtfl-stacktraces b/scripts/rtfl-stacktraces new file mode 100755 index 0000000..a9e953b --- /dev/null +++ b/scripts/rtfl-stacktraces @@ -0,0 +1,117 @@ +#!/usr/bin/perl + +# RTFL +# +# Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Usage: rtfl-stracktraces <method name> +# +# Prints stacktraces which lead to a specific method given as command line +# argument (based on "obj-enter" and "obj-leave"). +# +# Further Arguments: +# +# -s Short format. +# -n <n> Regard only stacktraces with at least <n> occurences of the method. +# -e <n> Do not print stack traces, but all messages; if a stacktrace would +# have been printed, exit after <n> further messages. Can be used +# together with -m. +# -m <mark> Do not print stack traces, but all messages; if a stacktrace would +# have been printed, edit a obj-mark with all parameters (file name, +# line number, process id, object, aspect, priority) taken from the +# last "obj-enter" command. Can be used together with -e. +# +# N. b. that parsing is incorrect, see <doc/rtfl.html#scripts>. + +sub helpAndExit { + die "Syntax: $0 [-s] [-n <n>] [-e <n>] [-m <mark>] <method name>"; +} + +$method = ""; +$short = 0; +$minNumCalls = 1; +$willEnd = 0; +$willEndCount = 0; +$mark = ""; + +for ($i = 0; $i < scalar @ARGV; $i++) { + if ($ARGV[$i] eq "-s") { + $short = 1; + } elsif ($ARGV[$i] eq "-n") { + helpAndExit if ($i == scalar @ARGV -1); + $minNumCalls = $ARGV[++$i]; + } elsif ($ARGV[$i] eq "-e") { + helpAndExit if ($i == scalar @ARGV -1); + $willEnd = 1; + $willEndCount = $ARGV[++$i]; + } elsif ($ARGV[$i] eq "-m") { + helpAndExit if ($i == scalar @ARGV -1); + $mark = $ARGV[++$i]; + } else { + $method = $ARGV[$i]; + } +} + +helpAndExit if ($method eq ""); + +@stack = (); +$first = 1; +$numCalls = 0; + +while(<STDIN>) { + if (($mark ne "" || $willEnd) && (!$endSoon || $endCount > 0)) { + print; + if ($endSoon && /^\[rtfl[^\]]*\]/) { $endCount--; }; + } + + if (/^(\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*):(obj-)?enter:([^:]*:[^:]*:[^:]*):([^:]*):.*$/) { + push @stack, ($short ? $4 : $_); + + if ($4 eq $method) { + $numCalls++; + + if ($numCalls >= $minNumCalls) { + if ($willEnd) { + if (!$endSoon) { + $endSoon = 1; + $endCount = $willEndCount; + } + } elsif ($mark ne "") { + print "$2:(obj-)?mark:$3:$mark\n"; + } else { + if ($short) { + $firstInLine = 1; + foreach $frame (@stack) { + print " > " unless $firstInLine; + print $frame; + $firstInLine = 0; + } + print "\n"; + } else { + print "-" x 79, "\n" unless $first; + foreach $frame (@stack) { print $frame; } + } + $first = 0; + } + } + } + } elsif (/^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?leave:.*$/) { + $l = pop @stack; + if ($l =~ + /^\[rtfl[^\]]*\][^:]*:[^:]*:[^:]*:(obj-)?enter:[^:]*:[^:]*:[^:]*:$method:.*$/) + { $numCalls--; } + } +} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..e63bcbc --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,137 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/tests"' + +noinst_PROGRAMS = \ + rtfl-cat \ + rtfl-trickle \ + test-pipes-1 \ + test-select-1 \ + test-version-cmp \ + test-fltk-1 \ + test-fltk-2 \ + test-rtfl-objects-1-without-rtfl \ + test-rtfl-objects-1-with-rtfl \ + test-rtfl-objects-2-without-rtfl \ + test-rtfl-objects-2-with-rtfl \ + test-rtfl-objects-3-without-rtfl \ + test-rtfl-objects-3-with-rtfl \ + test-rtfl-stats-1-without-rtfl \ + test-rtfl-stats-1-with-rtfl \ + test-tools-1 \ + test-tools-2 \ + test-tools-3 \ + test-tools-4 \ + test-tools-5 \ + test-tools-6 \ + test-widgets-1 \ + test-widgets-2 \ + test-widgets-3 \ + test-widget-b-splines + +if HAS_GRAPHVIZ +noinst_PROGRAMS += \ + test-graphviz-1 +endif + +rtfl_cat_SOURCES = rtfl_cat.c + +rtfl_trickle_SOURCES = rtfl_trickle.c + +test_pipes_1_SOURCES = test_pipes_1.c + +test_select_1_SOURCES = test_select_1.c + +test_version_cmp_SOURCES = test_version_cmp.c + +test_fltk_1_SOURCES = test_fltk_1.cc +test_fltk_1_LDADD = @LIBFLTK_LIBS@ + +test_fltk_2_SOURCES = test_fltk_2.cc +test_fltk_2_LDADD = @LIBFLTK_LIBS@ + +test_graphviz_1_SOURCES = test_graphviz_1.c +test_graphviz_1_LDADD = @GRAPHVIZ_LIBS@ + +test_rtfl_objects_1_without_rtfl_SOURCES = test_rtfl_objects_1.cc +test_rtfl_objects_1_without_rtfl_LDADD = ../lout/liblout.a +test_rtfl_objects_1_with_rtfl_SOURCES = test_rtfl_objects_1_with_rtfl.cc +test_rtfl_objects_1_with_rtfl_LDADD = ../lout/liblout.a + +test_rtfl_objects_2_without_rtfl_SOURCES = test_rtfl_objects_2.cc +test_rtfl_objects_2_with_rtfl_SOURCES = test_rtfl_objects_2_with_rtfl.cc + +test_rtfl_objects_3_without_rtfl_SOURCES = test_rtfl_objects_3.cc +test_rtfl_objects_3_with_rtfl_SOURCES = test_rtfl_objects_3_with_rtfl.cc + +test_rtfl_stats_1_without_rtfl_SOURCES = test_rtfl_stats_1.cc +test_rtfl_stats_1_without_rtfl_LDADD = ../lout/liblout.a +test_rtfl_stats_1_with_rtfl_SOURCES = test_rtfl_stats_1_with_rtfl.cc +test_rtfl_stats_1_with_rtfl_LDADD = ../lout/liblout.a + +test_tools_1_SOURCES = test_tools_1.cc +test_tools_1_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_tools_2_SOURCES = test_tools_2.cc simple_sink.hh simple_sink.cc \ + testtools.hh testtools.cc +test_tools_2_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_tools_3_SOURCES = test_tools_3.cc simple_sink.hh simple_sink.cc +test_tools_3_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_tools_4_SOURCES = test_tools_4.cc simple_sink.hh simple_sink.cc \ + testtools.hh testtools.cc +test_tools_4_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_tools_5_SOURCES = test_tools_5.cc simple_sink.hh simple_sink.cc \ + testtools.hh testtools.cc +test_tools_5_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_tools_6_SOURCES = test_tools_6.cc simple_sink.hh simple_sink.cc \ + testtools.hh testtools.cc +test_tools_6_LDADD = \ + ../common/librtfl-tools.a \ + ../lout/liblout.a + +test_widgets_1_SOURCES = test_widgets_1.cc +test_widgets_1_LDADD = \ + ../dwr/libDw-rtfl.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + +test_widgets_2_SOURCES = test_widgets_2.cc +test_widgets_2_LDADD = \ + ../dwr/libDw-rtfl.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + +test_widgets_3_SOURCES = test_widgets_3.cc +test_widgets_3_LDADD = \ + ../dwr/libDw-rtfl.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + +test_widget_b_splines_SOURCES = test_widget_b_splines.cc +test_widget_b_splines_LDADD = \ + ../dwr/libDw-rtfl.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + diff --git a/tests/rtfl_cat.c b/tests/rtfl_cat.c new file mode 100644 index 0000000..65d2e06 --- /dev/null +++ b/tests/rtfl_cat.c @@ -0,0 +1,53 @@ +/* + * RTFL + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Simply copy stdin to stdout, with some debug messages printed to + * stderr. Used to test rtfl-tee. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +static void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +int main (int argc, char *argv[]) +{ + char buf[2048]; + ssize_t n; + + do { + if ((n = read (0, buf, sizeof (buf))) == -1) syserr ("read failed"); + fprintf (stderr, "==> [%s] n = %d\n", argv[0], (int)n); + if (write (1, buf, n) == -1) syserr ("write failed"); + } while (n > 0); + + return 0; +} diff --git a/tests/rtfl_trickle.c b/tests/rtfl_trickle.c new file mode 100644 index 0000000..391534e --- /dev/null +++ b/tests/rtfl_trickle.c @@ -0,0 +1,55 @@ +/* + * RTFL + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Simply copy stdin to stdout, but slowly character by character. + * Used to test rtfl-tee. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +static void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +int main (int argc, char *argv[]) +{ + char buf[2048]; + ssize_t n, i; + + do { + if ((n = read (0, buf, sizeof (buf))) == -1) syserr ("read failed"); + for (i = 0; i < n; i++) { + if (write (1, buf + i, 1) == -1) syserr ("write failed"); + usleep (250000); + } + } while (n > 0); + + return 0; +} diff --git a/tests/simple_sink.cc b/tests/simple_sink.cc new file mode 100644 index 0000000..d27fd18 --- /dev/null +++ b/tests/simple_sink.cc @@ -0,0 +1,61 @@ +#include "simple_sink.hh" +#include "common/tools.hh" + +#include <unistd.h> +#include <sys/timeb.h> + +namespace rtfl { + +namespace tests { + +using namespace rtfl::tools; + +SimpleSink::SimpleSink () +{ + startTime = getCurrentTime (); + msg ("<init>"); +} + +void SimpleSink::setLinesSource (LinesSource *source) +{ + msg ("setLinesSource: souce = %p", source); +} + +void SimpleSink::processLine (char *line) +{ + msg ("processLine: %s", line); +} + +void SimpleSink::timeout (int type) +{ + msg ("timeout: type = %d", type); +} + +void SimpleSink::finish () +{ + msg ("finish"); +} + +long SimpleSink::getCurrentTime () +{ + struct timeb t; + if (ftime (&t) == -1) + syserr ("ftime() failed"); + return t.time * 1000L + t.millitm; +} + +void SimpleSink::msg (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + + long time = getCurrentTime () - startTime; + printf ("[SimpleSink] %2ld.%03ld -- ", time / 1000, time % 1000); + + vprintf (fmt, args); + putchar ('\n'); +} + +} // namespace tests + +} // namespace rtfl diff --git a/tests/simple_sink.hh b/tests/simple_sink.hh new file mode 100644 index 0000000..fb0a87a --- /dev/null +++ b/tests/simple_sink.hh @@ -0,0 +1,30 @@ +#ifndef __TESTS_SIMPLE_SINK_HH__ +#define __TESTS_SIMPLE_SINK_HH__ + +#include "common/lines.hh" + +namespace rtfl { + +namespace tests { + +class SimpleSink: public rtfl::tools::LinesSink { +private: + long getCurrentTime (); + void msg (const char *fmt, ...); + + long startTime; + +public: + SimpleSink (); + + void setLinesSource (rtfl::tools::LinesSource *source); + void processLine (char *line); + void timeout (int type); + void finish (); +}; + +} // namespace tests + +} // namespace rtfl + +#endif // __TESTS_SIMPLE_SINK_HH__ diff --git a/tests/test_fltk_1.cc b/tests/test_fltk_1.cc new file mode 100644 index 0000000..84d1cac --- /dev/null +++ b/tests/test_fltk_1.cc @@ -0,0 +1,64 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl_Window.H> +#include <FL/Fl_Menu_Bar.H> +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl.H> + +Fl_Window *window; +Fl_Menu_Bar *menu; + +static void changed (Fl_Widget *widget, void *data); + +Fl_Menu_Item menuItems[] = { + { "&Week", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 }, + { "Monday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Tuesday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Wednesday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Thursday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Friday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Saturday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { "Sunday", 0, changed, NULL, FL_MENU_TOGGLE, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + +void changed (Fl_Widget *widget, void *data) +{ + printf ("Monday: %s\n", + menu->find_item("&Week/Monday")->value () ? "set" : "clear"); + printf ("Tuesday: %s\n", + menu->find_item("&Week/Tuesday")->value () ? "set" : "clear"); + printf ("etc.\n"); +} + +int main(int argc, char **argv) +{ + window = new Fl_Window(100, 24, "FLTK Test 1"); + window->box(FL_NO_BOX); + window->begin(); + + menu = new Fl_Menu_Bar(0, 0, 100, 24); + menu->copy(menuItems); + + window->show(); + int errorCode = Fl::run(); + return errorCode; +} diff --git a/tests/test_fltk_2.cc b/tests/test_fltk_2.cc new file mode 100644 index 0000000..6dacce3 --- /dev/null +++ b/tests/test_fltk_2.cc @@ -0,0 +1,47 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl_Window.H> +#include <FL/Fl_Hold_Browser.H> +#include <FL/Fl.H> + +Fl_Window *window; +Fl_Hold_Browser *browser; + +int main(int argc, char **argv) +{ + window = new Fl_Window(500, 500, "FLTK Test 2"); + window->begin(); + + browser = new Fl_Hold_Browser (0, 0, 500, 500, NULL); + + for (int n = 0; n < 255; n++) { + char buf[256]; + + snprintf (buf, sizeof(buf), "@B%d@.0x%02x - %d", n, n, n); + browser->add (buf, NULL); + + snprintf (buf, sizeof(buf), "@i@B%d@.0x%02x - %d", n, n, n); + browser->add (buf, NULL); + } + + window->show(); + int errorCode = Fl::run(); + return errorCode; +} diff --git a/tests/test_graphviz_1.c b/tests/test_graphviz_1.c new file mode 100644 index 0000000..9199b25 --- /dev/null +++ b/tests/test_graphviz_1.c @@ -0,0 +1,46 @@ +#include <graphviz/gvc.h> + +int main(int argc, char *argv[]) +{ + Agnode_t *node1, *node2; + Agedge_t *edge1; + Agraph_t *graph; + GVC_t *gvc; + + gvc = gvContext (); + graph = agopen ("graph", Agdirected, NULL); + + node1 = agnode(graph, "node1", TRUE); + agsafeset (node1, "width", "1", ""); + agsafeset (node1, "height", "1", ""); + + node2 = agnode(graph, "node2", TRUE); + agsafeset (node2, "width", "1", ""); + agsafeset (node2, "height", "1", ""); + + edge1 = agedge(graph, node1, node2, "edge1", TRUE); + + puts ("---------- initially ----------"); + agwrite (graph, stdout); + + gvLayout (gvc, graph, "dot"); + gvRender(gvc, graph, "dot", NULL); + gvFreeLayout(gvc, graph); + + puts ("---------- after first layouting ----------"); + agwrite (graph, stdout); + + agsafeset (node2, "height", "2", ""); + + gvLayout (gvc, graph, "dot"); + gvRender(gvc, graph, "dot", NULL); + gvFreeLayout(gvc, graph); + + puts ("---------- after second layouting ----------"); + agwrite (graph, stdout); + + agclose (graph); + gvFreeContext(gvc); + + return 0; +} diff --git a/tests/test_pipes_1.c b/tests/test_pipes_1.c new file mode 100644 index 0000000..89e3256 --- /dev/null +++ b/tests/test_pipes_1.c @@ -0,0 +1,76 @@ +/* + * RTFL + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> + +static void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +int main (int argc, char *argv[]) +{ + int parent2child[2]; + char buf[2048]; + ssize_t n; + + if (pipe (parent2child) == -1) syserr ("pipe failed"); + + switch (fork ()) { + case -1: + syserr ("fork failed"); + break; + + case 0: + if (close (parent2child[1]) == -1) + syserr ("close(%d) failed", parent2child[0]); + if (close (0) == -1) syserr ("close(0) failed"); + if (dup2 (parent2child[0], 0) == -1) + syserr ("dup2(%d, 0) failed", parent2child[0]); + if (close (parent2child[0]) == -1) + syserr ("close(%d) failed", parent2child[0]); + + do { + if ((n = read (0, buf, sizeof (buf))) == -1) syserr ("read failed"); + fprintf (stderr, "[child] %d bytes read\n", (int)n); + } while (n > 0); + break; + + default: + if (close (parent2child[0]) == -1) + syserr ("close(%d) failed", parent2child[0]); + if (write (parent2child[1], "Hi!", 3) == -1) syserr ("write failed"); + if (close (parent2child[1]) == -1) + syserr ("close(%d) failed", parent2child[0]); + + break; + } + + return 0; +} diff --git a/tests/test_rtfl_objects_1.cc b/tests/test_rtfl_objects_1.cc new file mode 100644 index 0000000..11e16c4 --- /dev/null +++ b/tests/test_rtfl_objects_1.cc @@ -0,0 +1,118 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include "debug_rtfl.hh" +#include "lout/object.hh" +#include "lout/container.hh" + +using namespace lout::object; +using namespace lout::container::typed; + +class A +{ +private: + A *other; + List<Integer> *numbers; + +public: + A (); + ~A (); + void setOther (A *other); + int doSomething (int n); +}; + +class B: public A +{ +public: + B (); +}; + +class C: public A +{ +public: + C (); +}; + + +A::A () +{ + DBG_OBJ_CREATE ("A"); + + other = NULL; + numbers = new List<Integer> (true); +} + +A::~A () +{ + delete numbers; +} + +void A::setOther (A *other) +{ + DBG_OBJ_ASSOC_CHILD (other); + + this->other = other; +} + +int A::doSomething (int n) +{ + DBG_OBJ_ENTER ("", 0, "doSomething", "%d", n); + + DBG_OBJ_MSGF ("", 0, "some message: n = %d", n); + + int r = random () % 251; + numbers->append (new Integer (r)); + DBG_OBJ_ARRSET_NUM ("numbers", numbers->size () - 1, r); + + if (other && n > 0) + other->doSomething (n - 1); + + DBG_OBJ_LEAVE_VAL ("%d", r); + return r; +} + +B::B () +{ + DBG_OBJ_CREATE ("B"); +} + +C::C () +{ + DBG_OBJ_CREATE ("C"); +} + +int main (int argc, char *argv[]) +{ + DBG_OBJ_CLASS_COLOR ("A", "#ffa0a0"); + DBG_OBJ_CLASS_COLOR ("B", "#60ff60"); + DBG_OBJ_CLASS_COLOR ("C", "#b0b0ff"); + + A x; + B y; + C z; + + x.setOther (&y); + y.setOther (&z); + z.setOther (&x); + + x.doSomething (8); + + return 0; +} diff --git a/tests/test_rtfl_objects_1_with_rtfl.cc b/tests/test_rtfl_objects_1_with_rtfl.cc new file mode 100644 index 0000000..c1eb983 --- /dev/null +++ b/tests/test_rtfl_objects_1_with_rtfl.cc @@ -0,0 +1,2 @@ +#define DBG_RTFL +#include "test_rtfl_objects_1.cc" diff --git a/tests/test_rtfl_objects_2.cc b/tests/test_rtfl_objects_2.cc new file mode 100644 index 0000000..1cd134e --- /dev/null +++ b/tests/test_rtfl_objects_2.cc @@ -0,0 +1,69 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include "../debug_rtfl.hh" + +class A +{ +private: + int a; + +public: + inline A (int a) { DBG_OBJ_CREATE ("x::A"); this->a = a; } + inline void behaveAsA () { DBG_OBJ_MSGF ("", 0, "behaveAsA: a = %d", a); } +}; + +class B +{ +private: + int b; + +public: + inline B (int b) { DBG_OBJ_CREATE ("y::B"); this->b = b; } + inline void behaveAsB () { DBG_OBJ_MSGF ("", 0, "behaveAsB: b = %d", b); } +}; + +class C: public A, public B +{ +private: + int c; + +public: + inline C (int c): A (c / 3), B (c / 2) { + DBG_OBJ_CREATE ("z::C"); + DBG_OBJ_BASECLASS (A); + DBG_OBJ_BASECLASS (B); + this->c = c; + } + inline void behaveAsC () { DBG_OBJ_MSGF ("", 0, "behaveAsC: c = %d", c); } +}; + + +int main (int argc, char *argv[]) +{ + DBG_OBJ_CLASS_COLOR ("x::A", "#c0ff80"); + DBG_OBJ_CLASS_COLOR ("y::B", "#c0c0ff"); + DBG_OBJ_CLASS_COLOR ("z::C", "#ffa0a0"); + + C c (6); + c.behaveAsA (); + c.behaveAsB (); + c.behaveAsC (); +} diff --git a/tests/test_rtfl_objects_2_with_rtfl.cc b/tests/test_rtfl_objects_2_with_rtfl.cc new file mode 100644 index 0000000..2158044 --- /dev/null +++ b/tests/test_rtfl_objects_2_with_rtfl.cc @@ -0,0 +1,2 @@ +#define DBG_RTFL +#include "test_rtfl_objects_2.cc" diff --git a/tests/test_rtfl_objects_3.cc b/tests/test_rtfl_objects_3.cc new file mode 100644 index 0000000..6cbeb4f --- /dev/null +++ b/tests/test_rtfl_objects_3.cc @@ -0,0 +1,85 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include "../debug_rtfl.hh" + +class A +{ +private: + A *otherA; + +public: + A (); + ~A (); + void setOtherA (A *newOtherA); + void doSomething (int level); + void doActualStuff (); +}; + +A::A () +{ + DBG_OBJ_CREATE ("A"); + otherA = NULL; +} + +A::~A () +{ + DBG_OBJ_DELETE (); +} + +void A::setOtherA (A *newOtherA) +{ + DBG_OBJ_ENTER ("all", 0, "setOtherA", "%p", newOtherA); + + otherA = newOtherA; + DBG_OBJ_ASSOC_CHILD (otherA); + + DBG_OBJ_LEAVE (); +} + +void A::doSomething (int level) +{ + DBG_OBJ_ENTER ("all", 0, "doSomething", "%d", level); + + if (level > 0) { + doActualStuff (); + if (otherA) + otherA->doSomething (level - 1); + } + + DBG_OBJ_LEAVE (); +} + +void A::doActualStuff () +{ + DBG_OBJ_ENTER0 ("all", 0, "doActualStuff"); + + DBG_OBJ_MSG ("all", 1, "(pretending ...)"); + + DBG_OBJ_LEAVE (); +} + +int main (int argc, char *argv[]) +{ + A a1, a2; + a1.setOtherA (&a2); + a2.setOtherA (&a1); + a1.doSomething (20); +} diff --git a/tests/test_rtfl_objects_3_with_rtfl.cc b/tests/test_rtfl_objects_3_with_rtfl.cc new file mode 100644 index 0000000..032f6aa --- /dev/null +++ b/tests/test_rtfl_objects_3_with_rtfl.cc @@ -0,0 +1,2 @@ +#define DBG_RTFL +#include "test_rtfl_objects_3.cc" diff --git a/tests/test_rtfl_stats_1.cc b/tests/test_rtfl_stats_1.cc new file mode 100644 index 0000000..9be443d --- /dev/null +++ b/tests/test_rtfl_stats_1.cc @@ -0,0 +1,54 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +// See "collect-stats" in this directory. + +#include "debug_rtfl.hh" +#include "lout/object.hh" +#include "lout/container.hh" + +using namespace lout::object; +using namespace lout::container::typed; + + +static void sortvec (Vector<Integer> *vec) +{ + DBG_GEN_TIME (); + DBG_OBJ_ENTER0_O ("", 0, NULL, "sortvec"); + + DBG_OBJ_MSGF_O ("", 0, NULL, "size = %d", vec->size ()); + + vec->sort (); + + DBG_GEN_TIME (); + DBG_OBJ_LEAVE_O (NULL); +} + +int main (int argc, char *argv[]) +{ + for (int i = 0; i < 20; i++) { + int n = 20000 * i; + Vector<Integer> vec (n, true); + for (int j = 0; j < n; j++) + vec.put (new Integer (random ())); + sortvec (&vec); + } + + return 0; +} diff --git a/tests/test_rtfl_stats_1_with_rtfl.cc b/tests/test_rtfl_stats_1_with_rtfl.cc new file mode 100644 index 0000000..da23c75 --- /dev/null +++ b/tests/test_rtfl_stats_1_with_rtfl.cc @@ -0,0 +1,2 @@ +#define DBG_RTFL +#include "test_rtfl_stats_1.cc" diff --git a/tests/test_select_1.c b/tests/test_select_1.c new file mode 100644 index 0000000..db92d06 --- /dev/null +++ b/tests/test_select_1.c @@ -0,0 +1,67 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> + +static void syserr (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, ": %s\n", strerror (errno)); + exit (1); +} + +int main (int argc, char *argv[]) +{ + int eos = 0; + while (!eos) { + fd_set readfds; + FD_ZERO (&readfds); + FD_SET (0, &readfds); + + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + + if (select (1, &readfds, NULL, NULL, &tv) == -1) + syserr ("select failed"); + + if (FD_ISSET (0, &readfds)) { + char buf[1024]; + int n = read (0, buf, 1024); + if (n == -1) + syserr ("select read"); + else if (n == 0) + eos = 1; + else + if (write (1, buf, n) == -1) + syserr ("write failed"); + } else + puts ("---------- timeout? ----------"); + } + + return 0; +} diff --git a/tests/test_tools_1.cc b/tests/test_tools_1.cc new file mode 100644 index 0000000..a09b7aa --- /dev/null +++ b/tests/test_tools_1.cc @@ -0,0 +1,97 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "common/tools.hh" + +using namespace lout::object; +using namespace lout::misc; +using namespace lout::container::untyped; +using namespace rtfl::tools; + +class TestObject: public Object +{ +private: + int n; + +public: + TestObject (int n); + ~TestObject (); + + void intoStringBuffer (StringBuffer *sb); +}; + +TestObject::TestObject (int n) +{ + this->n = n; +} + +TestObject::~TestObject () +{ + printf("(~TestObject: %d)\n", n); +} + +void TestObject::intoStringBuffer (StringBuffer *sb) +{ + sb->append ("TestObject: "); + sb->appendInt (n); +} + +int main(int argc, char **argv) +{ + String *id[4]; + TestObject *testObject[4]; + EquivalenceRelation rel (true, true); + + for (int i = 0; i < 4; i++) { + char idBuf[] = { 'i', (char)('1' + i), 0 }; + id[i] = new String(idBuf); + testObject[i] = new TestObject (i); + + // New key since they are deleted by EquivalenceRelation.remove() + // but later still needed for EquivalenceRelation.contains(). + rel.put (new String(id[i]->chars ()), testObject[i]); + } + + for (int i = 0; i < 4; i++) { + puts ("--------------------------------------------------"); + for (int j = 0; j < 4; j++) { + if (rel.contains (id[j])) { + Object *obj = rel.get (id[j]); + StringBuffer sb; + obj->intoStringBuffer (&sb); + puts (sb.getChars ()); + } else + puts ("(removed)"); + } + + if (i == 0) + rel.relate (id[0], id[1]); + + if (i == 1) + rel.relate (id[0], id[2]); + + if (i == 2) + rel.remove (id[0]); + } + + for (int i = 0; i < 4; i++) + delete id[i]; + + puts ("--------------------------------------------------"); +} diff --git a/tests/test_tools_2.cc b/tests/test_tools_2.cc new file mode 100644 index 0000000..0cf4cad --- /dev/null +++ b/tests/test_tools_2.cc @@ -0,0 +1,26 @@ +#include "simple_sink.hh" +#include "testtools.hh" + +using namespace rtfl::tools; +using namespace rtfl::tests; + +int main (int argc, char *argv[]) +{ + int fd1 = openPipe ("echo Hello; sleep 2"); + BlockingLinesSource s1 (fd1); + // Both commands start at the same time, even if fd2 is processed later; thus + // the "sleep" at the beginning of the second command. + int fd2 = openPipe ("sleep 2; echo World; sleep 2"); + BlockingLinesSource s2 (fd2); + + LinesSourceSequence lss (false); + lss.add (&s1); + lss.add (&s2); + lss.addTimeout(1, 123); + lss.addTimeout(3, 124); + + SimpleSink sink; + lss.setup (&sink); + + return 0; +} diff --git a/tests/test_tools_3.cc b/tests/test_tools_3.cc new file mode 100644 index 0000000..5762c90 --- /dev/null +++ b/tests/test_tools_3.cc @@ -0,0 +1,16 @@ +#include "simple_sink.hh" + +using namespace rtfl::tools; +using namespace rtfl::tests; + +int main (int argc, char *argv[]) +{ + BlockingLinesSource source (0); + for(int i = 0; i < 5; i++) + source.addTimeout(1 + i, 123 + i); + + SimpleSink sink; + source.setup (&sink); + + return 0; +} diff --git a/tests/test_tools_4.cc b/tests/test_tools_4.cc new file mode 100644 index 0000000..8727fa4 --- /dev/null +++ b/tests/test_tools_4.cc @@ -0,0 +1,19 @@ +#include "simple_sink.hh" +#include "testtools.hh" + +using namespace rtfl::tools; +using namespace rtfl::tests; + +int main (int argc, char *argv[]) +{ + int fd = + openPipe ("for i in $(seq 1 10); do echo Hello world $i; sleep 1; done"); + BlockingLinesSource source (fd); + for(int i = 0; i < 5; i++) + source.addTimeout(1 + i, 123 + i); + + SimpleSink sink; + source.setup (&sink); + + return 0; +} diff --git a/tests/test_tools_5.cc b/tests/test_tools_5.cc new file mode 100644 index 0000000..9f5f957 --- /dev/null +++ b/tests/test_tools_5.cc @@ -0,0 +1,37 @@ +#include "simple_sink.hh" +#include "testtools.hh" + +using namespace rtfl::tools; +using namespace rtfl::tests; + +class NotSoSimpleSink: public SimpleSink +{ +private: + LinesSource *source; + +public: + NotSoSimpleSink (LinesSource *source); + void processLine (char *line); +}; + +NotSoSimpleSink::NotSoSimpleSink (LinesSource *source) +{ + this->source = source; +} + +void NotSoSimpleSink::processLine (char *line) +{ + SimpleSink::processLine (line); + if (strcmp (line, "create") == 0) + source->addTimeout (2, 0); +} + +int main (int argc, char *argv[]) +{ + int fd = openPipe ("echo create; echo msg; sleep 5"); + BlockingLinesSource source (fd); + NotSoSimpleSink sink (&source); + source.setup (&sink); + + return 0; +} diff --git a/tests/test_tools_6.cc b/tests/test_tools_6.cc new file mode 100644 index 0000000..f02a19e --- /dev/null +++ b/tests/test_tools_6.cc @@ -0,0 +1,29 @@ +#include "simple_sink.hh" +#include "testtools.hh" + +using namespace rtfl::tools; +using namespace rtfl::tests; + +// Test LinesSourceSequence: make sure that LinesSourceSequence deals correctly +// with timeouts. +int main (int argc, char *argv[]) +{ + int fd1 = openPipe ("sleep 5"); + BlockingLinesSource s1 (fd1); + // Both commands start at the same time, even if fd2 is processed later; so + // it takes 10, not 15 secs totally. + int fd2 = openPipe ("sleep 10"); + BlockingLinesSource s2 (fd2); + + LinesSourceSequence lss (false); + lss.add (&s1); + lss.add (&s2); + + for (int i = 2; i <= 20; i += 2) + lss.addTimeout(i, i); + + SimpleSink sink; + lss.setup (&sink); + + return 0; +} diff --git a/tests/test_version_cmp.c b/tests/test_version_cmp.c new file mode 100644 index 0000000..98667d6 --- /dev/null +++ b/tests/test_version_cmp.c @@ -0,0 +1,49 @@ +// Used for "../configure.ac". + +#include <ctype.h> +#include <stdlib.h> + +static int version_cmp (const char *v1, const char *v2) +{ + const char *s1 = v1, *s2 = v2; + while (*s1 && *s2) { + if (isdigit (*s1) && isdigit (*s2)) { + char buf1[10], buf2[10]; + int n1 = 0, n2 = 0; + + while (isdigit (*s1)) { + if (n1 < 9) buf1[n1++] = *s1; + s1++; + } + + while (isdigit (*s2)) { + if (n2 < 9) buf2[n2++] = *s2; + s2++; + } + + buf1[n1] = buf2[n2] = 0; + int c = atoi (buf1) - atoi (buf2); + if (c != 0) + return c; + } else { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + } + + return *s1 - *s2; +} + +#include <stdio.h> + +int main (int argc, char *argv[]) +{ + printf ("%d, %d, %d %d\n", + version_cmp ("0.1", "0.001"), + version_cmp ("0.1", "0.002"), + version_cmp ("0.1a", "0.1"), + version_cmp ("2.38.0", "2.38.1")); + return 0; +} diff --git a/tests/test_widget_b_splines.cc b/tests/test_widget_b_splines.cc new file mode 100644 index 0000000..a4a5f02 --- /dev/null +++ b/tests/test_widget_b_splines.cc @@ -0,0 +1,123 @@ +/* + * RTFL + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#include <FL/Fl_Window.H> +#include <FL/Fl.H> + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" +#include "dwr/tools.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; +using namespace rtfl::dw::tools; + +class BSplineWidget: public Widget +{ +public: + void sizeRequestImpl (Requisition *requisition); + void getExtremesImpl(Extremes *extremes); + void draw (View *view, Rectangle *area); + Iterator *iterator (Content::Type mask, bool atEnd); +}; + +void BSplineWidget::sizeRequestImpl (Requisition *requisition) +{ + requisition->width = getAvailWidth (true); + requisition->ascent = getAvailHeight (true); + requisition->descent = 0; +} + +void BSplineWidget::getExtremesImpl(Extremes *extremes) +{ + extremes->minWidth = extremes->maxWidth = extremes->minWidthIntrinsic = + extremes->maxWidthIntrinsic = 1; +} + +void BSplineWidget::draw (View *view, Rectangle *area) +{ + double xp[7] = { 0.4, 0.1, 0.2, 0.5, 0.5, 0.9, 0.7 }; + double yp[7] = { 0.1, 0.5, 0.8, 0.7, 0.2, 0.4, 0.9 }; + int x[7], y[7]; + + for (int i = 0; i < 7; i++) { + x[i] = allocation.x + xp[i] * allocation.width; + y[i] = allocation.y + yp[i] * getHeight (); + } + + for(int i = 0; i < 7; i++) { + view->drawArc (getStyle()->color, core::style::Color::SHADING_NORMAL, + true, x[i], y[i], 5, 5, 0, 360); + } + + drawBSpline (view, getStyle(), 4, 7, x, y); +} + +Iterator *BSplineWidget::iterator (Content::Type mask, bool atEnd) +{ + return new core::EmptyIterator (this, mask, atEnd); +} + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(500, 400, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 500, 400); + layout->attachView (viewport); + + StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.padding.setVal (5); + + FontAttrs fontAttrs; + fontAttrs.name = "DejaVu Sans"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *style = Style::create (&styleAttrs); + + BSplineWidget *widget = new BSplineWidget (); + widget->setStyle (style); + layout->setWidget (widget); + style->unref(); + + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} diff --git a/tests/test_widgets_1.cc b/tests/test_widgets_1.cc new file mode 100644 index 0000000..c6e2fed --- /dev/null +++ b/tests/test_widgets_1.cc @@ -0,0 +1,142 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl_Window.H> +#include <FL/Fl.H> + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" + +#include "dwr/graph.hh" +#include "dwr/label.hh" +#include "dwr/hbox.hh" +#include "dwr/vbox.hh" +#include "dwr/toggle.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +using namespace rtfl::dw; + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(500, 400, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 500, 400); + layout->attachView (viewport); + + StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.padding.setVal (5); + + FontAttrs fontAttrs; + fontAttrs.name = "DejaVu Sans"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *graphStyle = Style::create (&styleAttrs); + Graph *graph = new Graph (); + graph->setStyle (graphStyle); + layout->setWidget (graph); + graphStyle->unref(); + + styleAttrs.borderWidth.setVal (1); + styleAttrs.setBorderStyle (BORDER_OUTSET); + styleAttrs.setBorderColor (Color::create (layout, 0x000000)); + Style *textblockStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.padding.setVal (0); + Style *textStyle = Style::create (&styleAttrs); + + graph->setRefStyle (textblockStyle); + + HBox *n1 = new HBox (false); + n1->setStyle (textblockStyle); + graph->addNode (n1); + + Label *l11 = new Label ("Hello <i>w<b>or</i>ld!</b>"); + l11->setStyle (textblockStyle); + n1->addChild (l11); + + Label *l12 = new Label ("More text ..."); + l12->setStyle (textblockStyle); + n1->addChild (l12); + + VBox *n2 = new VBox (false); + n2->setStyle (textblockStyle); + graph->addNode (n2); + + Label *l21 = new Label ("Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος ἦν πρὸς τὸν Θεόν, " + "καὶ Θεὸς ἦν ὁ Λόγος."); + l21->setStyle (textblockStyle); + n2->addChild (l21); + + Label *l22 = new Label ("Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν Θεόν. πάντα δι' αὐτοῦ " + "ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ " + "γέγονεν."); + l22->setStyle (textblockStyle); + n2->addChild (l22); + + Toggle *n3 = new Toggle (true); + n3->setStyle (textblockStyle); + graph->addNode (n3); + + Label *l31 = new Label ("small"); + l31->setStyle (textblockStyle); + n3->setSmall (l31); + + Label *l33 = new Label ("LLLAAAAARRRRRGGGEEE"); + l33->setStyle (textblockStyle); + n3->setLarge (l33); + + Label *n4 = new Label ("#4"); + n4->setStyle (textblockStyle); + graph->addNode (n4); + + graph->addEdge (n1, n2); + graph->addEdge (n1, n3); + graph->addEdge (n2, n1); + + textStyle->unref(); + textblockStyle->unref(); + + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} diff --git a/tests/test_widgets_2.cc b/tests/test_widgets_2.cc new file mode 100644 index 0000000..c20b50c --- /dev/null +++ b/tests/test_widgets_2.cc @@ -0,0 +1,97 @@ +/* + * RTFL + * + * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl_Window.H> +#include <FL/Fl.H> + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" + +#include "dwr/vbox.hh" +#include "dwr/label.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +using namespace rtfl::dw; + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(500, 400, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 500, 400); + layout->attachView (viewport); + + FontAttrs fontAttrs; + fontAttrs.name = "DejaVu Serif"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + + StyleAttrs styleAttrs; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); + styleAttrs.borderWidth.setVal (1); + styleAttrs.setBorderStyle (BORDER_OUTSET); + styleAttrs.padding.setVal (5); + styleAttrs.setBorderColor (Color::create (layout, 0x000000)); + styleAttrs.color = Color::create (layout, 0x000000); + + Style *boxStyle = Style::create (&styleAttrs); + + styleAttrs.margin.setVal (0); + styleAttrs.borderWidth.setVal (0); + styleAttrs.padding.setVal (0); + styleAttrs.setBorderColor (Color::create (layout, 0x000000)); + Style *labelStyle = Style::create (&styleAttrs); + + VBox *box = new VBox (false); + box->setStyle (boxStyle); + layout->setWidget (box); + + for (int i = 0; i < 100000; i++) { + char buf[32]; + sprintf (buf, "Label №%d", i); + Label *label = new Label (buf); + label->setStyle (labelStyle); + box->addChild (label); + } + + boxStyle->unref(); + labelStyle->unref(); + + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} diff --git a/tests/test_widgets_3.cc b/tests/test_widgets_3.cc new file mode 100644 index 0000000..5329200 --- /dev/null +++ b/tests/test_widgets_3.cc @@ -0,0 +1,97 @@ +/* + * RTFL + * + * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <FL/Fl_Window.H> +#include <FL/Fl.H> + +#include "dw/core.hh" +#include "dw/fltkcore.hh" +#include "dw/fltkviewport.hh" + +#include "dwr/vbox.hh" +#include "dwr/label.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +using namespace rtfl::dw; + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(500, 400, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 500, 400); + layout->attachView (viewport); + + FontAttrs fontAttrs; + fontAttrs.name = "DejaVu Serif"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + + StyleAttrs styleAttrs; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); + styleAttrs.borderWidth.setVal (1); + styleAttrs.setBorderStyle (BORDER_OUTSET); + styleAttrs.padding.setVal (5); + styleAttrs.setBorderColor (Color::create (layout, 0x000000)); + styleAttrs.color = Color::create (layout, 0x000000); + + Style *boxStyle = Style::create (&styleAttrs); + + styleAttrs.margin.setVal (0); + styleAttrs.borderWidth.setVal (0); + styleAttrs.padding.setVal (0); + styleAttrs.setBorderColor (Color::create (layout, 0x000000)); + Style *labelStyle = Style::create (&styleAttrs); + + VBox *box = new VBox (false); + box->setStyle (boxStyle); + layout->setWidget (box); + + for (int i = 0; i < 1000; i++) { + char buf[32]; + sprintf (buf, "Label №%d", i); + Label *label = new Label (buf); + label->setStyle (labelStyle); + box->addChild (label, i / 10); + } + + boxStyle->unref(); + labelStyle->unref(); + + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} diff --git a/tests/testtools.cc b/tests/testtools.cc new file mode 100644 index 0000000..175f482 --- /dev/null +++ b/tests/testtools.cc @@ -0,0 +1,49 @@ +#include "testtools.hh" +#include "common/tools.hh" + +#include <unistd.h> + +using namespace rtfl::tools; + +namespace rtfl { + +namespace tests { + +int openPipe (const char *command) +{ + int pipefd[2]; + + if (pipe (pipefd) == -1) + syserr ("pipe failed"); + + switch (fork ()) { + case -1: + syserr ("fork failed"); + break; + + case 0: + if (close (pipefd[0]) == -1) + syserr ("close(%d) failed", pipefd[0]); + if (close (1) == -1) + syserr ("close(%d) failed", 1); + if (dup2 (pipefd[1], 1) == -1) + syserr ("dup2(%d, %d) failed", pipefd[1], 1); + if (close (pipefd[1]) == -1) + syserr ("close(%d) failed", pipefd[1]); + execlp ("sh", "sh", "-c", command, NULL); + syserr ("exec(\"%s\", \"%s\", \"%s\", \"%s\", NULL) failed", + "sh", "sh", "-c", command); + break; + + default: + if (close (pipefd[1]) == -1) + syserr ("close(%d) failed", pipefd[1]); + return pipefd[0]; + } + + return -1; +} + +} // namespace tests + +} // namespace rtfl diff --git a/tests/testtools.hh b/tests/testtools.hh new file mode 100644 index 0000000..77c52da --- /dev/null +++ b/tests/testtools.hh @@ -0,0 +1,14 @@ +#ifndef __TESTS_TEST_TOOLS_HH__ +#define __TESTS_TEST_TOOLS_HH__ + +namespace rtfl { + +namespace tests { + +int openPipe (const char *command); + +} // namespace tests + +} // namespace rtfl + +#endif // __TESTS_TEST_TOOLS_HH__ |
