--- /dev/null
+# vi: ff=unix
+Frans Slothouber
+Jacco van Weert
+Petteri Kettunen
+Bernd Koesling
+Anthon Pang
+Thomas Aglassinger
+Stefan Kost
+David Druffner
+Nathan Prewitt
+Sasha Vasko
+Dennis Stampfer
+Je'rome Laheurte
+Janusz Piwowarski
+Andreas Signer
+Kai Hofmann
+Brian P. Hanley
+Gian Paolo Ciceri
+David White
+John P. Rouillard
+Jeremy Cowgar
+Gergely Budai
+
--- /dev/null
+ 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>.
--- /dev/null
+<XML enc="0" Ver="274.0">
+ <ProjectName value="ROBODoc"/>
+ <ProjectFilePath value="C:\Documents and Settings\Thuffir\Local Settings\Application Data\ComponentSoftware\CS-RCS\robodoc.cvs.sourceforge.net\robo/CVS.adm\MstData.stp"/>
+</XML>
--- /dev/null
+<XML Encode="1" Ver="274.0">
+ <x:0>
+ <x:08>
+ <![CDATA[^=lx^3>ff7N\80p~\86}}u\11pfYiuNPKM]]>
+ </x:08>
+ <x:04>
+ <![CDATA[H41ETb^]I4:@D>D\ eFnvfFI2P<]]>
+ </x:04>
+ <x:05>
+ <![CDATA[2rmK7cSY/09
+UWzoUPDCG]]>
+ </x:05>
+ <x:09>
+ <![CDATA[JrUhvd>>^0:
+MBP21U<t[]]>
+ </x:09>
+ <x:01>
+ <![CDATA[sFG`yJ39C9:;@ACDFCCC\13[GIW1OM[=]]>
+ </x:01>
+ <x:02>
+ <![CDATA[kLysF\0Rz0<
+ZF?ZA2nHs]]>
+ </x:02>
+ <x:03>
+ <![CDATA[owh^7cp@UBLcMQ_Rh^VCDIJLMNJLM\1cCZ<@2Gn4j]]>
+ </x:03>
+ </x:0>
+ <x:0>
+ <x:08>
+ <![CDATA[`GuEmb5zB=Jnxu{w\82\84\83s\87\83\87\89\17t2oRcmUC<]]>
+ </x:08>
+ <x:04>
+ <![CDATA[<?scXgN=?9=<D@FDFBJG\13IDq?9omE0]]>
+ </x:04>
+ <x:05>
+ <![CDATA[o99d;wn:D09
+t@D5FRgvF]]>
+ </x:05>
+ <x:09>
+ <![CDATA[rKzX;@WWo0:
+F9wH>>Nmt]]>
+ </x:09>
+ <x:06>
+ <x:12>
+ <x:07>
+ <![CDATA[D4B`F2nCy6}r\80rsw\81\10K;n9\aBxb]]>
+ </x:07>
+ <x:10>
+ <![CDATA[1NjAsGxaT09
+_I[YCr5L8]]>
+ </x:10>
+ <x:13>
+ <![CDATA[Rsb6_:yB3/ k5BhqyVze]]>
+ </x:13>
+ </x:12>
+ </x:06>
+ </x:0>
+ <ProjectName value="ROBODoc"/>
+ <ProjectFilePath value="C:\Documents and Settings\Thuffir\Local Settings\Application Data\ComponentSoftware\CS-RCS\robodoc.cvs.sourceforge.net\robo/CVS.adm\UsgData.stp"/>
+</XML>
--- /dev/null
+<XML>
+ <cacheHeadRevision value="1"/>
+ <keyExpand value="1"/>
+ <sharedWorkFolder value="0"/>
+ <symbNameOnCI value="0"/>
+ <keyExpandLT value="0"/>
+ <doQuickLock value="0"/>
+ <workingMode value="2"/>
+ <diffMode value="1"/>
+ <diffWrap value="-1"/>
+ <projLog value="0"/>
+ <infoCacheTimeout value="25"/>
+ <useGlobalPrjList value="0"/>
+ <recalcStatusOnScan value="0"/>
+ <winExpIntegration value="0"/>
+ <Ver value="274.0"/>
+ <ProjectName value="ROBODoc"/>
+ <ProjectFilePath value="C:\Documents and Settings\Thuffir\Application Data\ComponentSoftware\CS-RCS\ROBODoc\robo/CVS.adm\prjprm.stp"/>
+ <WorkflowInherit value="1"/>
+</XML>
--- /dev/null
+# vi: spell ff=unix
+
+Jun 2007 - V4.99.34
+ Gergely Budai
+ added option --header_breaks, which allows ROBODoc to insert line breaks
+ after every specified number of header names to improve readability.
+
+ Frans Slothouber
+ o Merged path by Tom Keal that Fixes bug in parsing of the robodoc.rc file.
+ (The wrong number of items is recorded for the preformatted_items and
+ format_items block.)
+
+Apr 2007 - V4.99.32
+ o Fixed bug 1706446, --sectionnameonly and --rtf format problem
+ o Fixed bug 1679101, "robodoc crashed on a faulty header".
+ o Added the option --no_subdirectories.
+ o output mode pipes at the end of an item don't result in broken
+ formatting anymore.
+ o Items that start with non-ascii characters are now recognized.
+ o makefile.plain works again
+ o the item names in the 'item order', 'ignore items', and other items blocks
+ in the robodoc.rc file are checked against the list of allowed item names.
+ o the options --sections and --toc do no longer lead
+ to an assert() with --ascii
+ o Added item sorting. The order in which items are to
+ appear in the documentation can be specified in the
+ robodoc.rc file.
+
+Feb 2007 - V4.99.30
+ Frans Slothouber
+ o Removed form-feed from ASCII output
+ o Applied patch from Christian Vigh to make ROBODoc compile
+ under MSVC++ V7
+ o Applied patch from Wim van Hoydonck to fix a bug
+ in the escaping of '_' in the names internal artifacts
+ for LaTeX output.
+ o Fixed makefile.plain
+ o ROBODoc compiles without warnings using
+ make -f makefile.plain
+
+ Gergely Budai
+ o Using bitfields in stead of and-ing with a define.
+ o Function name separator can be configured to be something
+ different than ','
+ o Added a configuration block that allows the used to
+ specify which items are pre-formatted by default.
+
+Sep 2006
+ Frans Slothouber
+ o Corrected return value of main()
+
+Sep 2006 - V4.99.28
+ Michèle Garoche
+ o Improved Mac support (for FINK)
+
+Aug 2006 - V4.99.28
+ Gergely Budai
+ o Fixed many compiler warnings.
+ o More readable .css file
+ o makefile for mingw under cygwin
+ o Better syntax colouring support
+
+ Frans Slothouber
+ o Moved most of the HTML formatting to the .css file
+ o Fixed a bug that caused the second list in a paragraph not
+ to be recognized.
+ o Added more documentation to the source.
+ o Removed code for SGML docbook
+ o Removed makefile and project file for MS VC++
+ o Added functions to check for duplicate and
+ mutual exclusive options.
+
+Jun 2006 - V4.99.26
+ o Merged patch from Gergely Budai. (Adds syntax colouring, better
+ Latex output, and better tab control).
+ o changed output of --test format to xml. (Easier to parse).
+ o Masterindex no longer contains links to source files when
+ --one_file_per_header is used.
+
+Jun 2006 - V4.99.24
+ o Added option --one_file_per_header (experimental: this splits
+ the documentation into many files, one per header).
+ o Added header examples to documentation.
+
+Dec 2005 - V4.99.23
+ o Made it compile again under MingW
+ o Fixed a bug in the reporting of a missing robodoc.rc file
+
+Dec 2005 - V4.99.21
+ o Fixed crash on empty source items.
+ o Merged patch from cdumke for handling long lines
+
+Dec 2005 - V4.99.19
+ o Merged a patch from Friedrich Haase that allows '--src .'
+ and adds the options --sectionnameonly
+ o Merged a patch from Andreas Weder for better LaTeX output
+ o Added configuration options to allow some items
+ to work in the same way as a SOURCE item.
+ o ROBODoc now allows module and function names
+ that include white space.
+
+Frans Slothouber
+Apr 2005 - V4.99.17
+ o Added patch by Thierry Pierron for troff output.
+
+Feb 2005 - V4.99.15
+ o Documented more tests.
+ o Changed timestamp format.
+ o Added check on fork(). If fork() is not found only a stub for robohdrs
+ is compiled.
+ o Added patch by Thierry Pierron for aliasses. A single
+ header can now be used to documents several similar functions/
+ classes/variables etc.
+
+Frans Slothouber
+Feb 2005 - V4.99.10
+ o Fixed a bug in the compilation under mingw
+
+Jan 2005 - V4.99.8
+Frans Slothouber
+ o ROBODoc will look in different locations for the robodoc.rc file.
+ ( $HOME/robodoc.rc, $HOMEDRIVE$HOMEPATH/robodoc.rc, and
+ /usr/share/robodoc/robodoc.rc )
+ o Added option --nogeneratedwith to enable users to remove the
+ generated with robodoc line for the generated documentation.
+ o Fixed the makefile for mingw (thanks to Brian Elmegaard)
+ o Fixed a bug that caused robodoc to find headers even if
+ there were none. [1055886]
+ o Fixed a bug that caused robodoc to crash if single line source
+ item was found. [1056681]
+
+Sep 2004 - V4.99.6
+ o Fixed a bug that caused robodoc to crash when no files
+ are found. [1033913]
+
+Sep 2004 - V4.99.4
+Frans Slothouber
+ o Fixed a bug that caused robodoc to die with an assert
+ when an absolute path is used under windows. [1033644]
+ o Updated the man page.
+ o Fixed a bug that caused links to be created for 'Foo' in
+ Bar_Foo.
+
+Aug 2004 - V4.99.3
+Frans Slothouber
+o If paths such as c:\doc\foo\bar are used as parameters
+ they are first translated to c:/doc/foo/bar before
+ they are further processed.
+o Added patch send in by Ernst Bokkelkamp that adds
+ the header type to each headers documentation.
+o Updated documentation.
+
+May 2004 - V4.99.1
+ o Merged a patch by Bernhard Schoenhammer that adds a
+ accept files:
+ block to the robodoc.rc file.
+ o Solved bug [972456] Case problem with creating links.
+ o Fixed a bug [975543] that caused the Doc dir to be included when it was
+ a subdir of the sourcedir and the sourcedir was specified to
+ be '.'
+
+May 2004 - V4.99.0
+ o Fixed XML DocBook output, it appears that 4.0.24 created
+ invalid XML files.
+ o Added smart formatting. Instead of using preformatted text,
+ that is copying the header text verbatim into the output document,
+ ROBODoc tries to determine the structure of the text from
+ the indentation and use of special characters.
+
+ For instance
+ This is a list:
+ o list item 1,
+ o list item 2.
+
+ Will be translated into the appropriate commands to create a similar
+ list in the output format.
+
+May 2004 - V4.0.25
+ Frans Slothouber
+ o Added --headless and --footless option to create documents that can be
+ included into large documents. (Works for LaTeX and HTML).
+
+May 2004 - V4.0.24
+ Frans Slothouber
+ o Added --nosort option
+ o With the define IGNORE_CASE_FILENAMES one can make robodoc case-insensitve
+ when it comes to filenames.
+ o Fixed bug [924530] False warning with empty lines
+ o Fixed bug [925920] broken robodoc.rc file; robodoc ignored end markers or
+ remark markers if there was no header marker block.
+
+March 2004 - V4.0.23
+ Frans Slothouber
+ o When sorting module headers come before headers
+ of other types.
+ o Fixes a bug in the sorting of headers.
+ o Fixes a bug in linking
+ o Sourcefiles that do not contain headers are not shown.
+
+February 2004 - V4.0.21
+ Frans Slothouber
+ o Added navigation bar for every header. It allows one to jump
+ to the parent header, the top of the document, and the index
+ when available.
+ o ROBODoc will continue parsing the other headers if it finds
+ a header it can not parse.
+ o Better formatted warnings.
+ o The sourcefile index is now always sorted
+ o Everything is sorted according to a non case sensitive string
+ compare.
+
+January 2004 - V4.0.19
+ Frans Slothouber
+ o Fixed bug 874567 No crosslinks for names that start with '-'
+ o Fixed bug 875533 No bolding for names starting with ::
+ o Added configurable header markers, remark markers,
+ and end markers. ROBODoc is now usable with
+ _any_ language.
+
+ Hermann Hueni
+ o Fixed broken configure on Solaris.
+
+January 2004 - V4.0.18
+ Frans Slothouber
+ o Update of the user manual and documentation
+ o Note that:
+ If you were using an 'extensions:' block in your robodoc.rc file,
+ you have to replace this with an 'ignore files:' block with all
+ extensions prefixed with '*'. So, if you were using
+
+ extensions:
+ .txt
+ .obj
+
+ replace this with
+
+ ignore files:
+ *.txt
+ *.obj
+
+December 2003 - V4.0.17
+ Frans Slothouber
+ o Added directory tree shaped master index for the sourcefiles.
+ o Added filtering of files and directories based on wildcard
+ expressions.
+ Michael Felke
+ o Support for lotus script, clean-up of headers.c and headers.h
+
+December 2003 - V4.0.15
+ Frans Slothouber
+ o Added support for DB/C
+
+December 2003 - V4.0.14
+ John Rouillard
+ o Bug fix. no index file was generated for headertypes of which
+ there were only internal headers.
+ Frans Slothouber
+ o More tests
+
+December 2003 - V4.0.13
+ Skipped
+
+December 2003 - V4.0.12
+ David White
+ o Updated makefile for borland C
+ o Added marker for Aspen Custom Modeller (ACM)
+ Frans Slothouber
+ o Added more documentation
+ o a .css file is now also used in singledoc mode
+ o each header in the html documentation now has a permanent
+ "a name" based on the full name of a header.
+ o fixed the title of singledoc documents.
+ o files or directories specified with --src or --doc
+ are now automatically prefixed with a ./
+ o It is now allowed to have a Doc directory that is a subdirectory
+ of the source directory. This Doc directory is skipped while robodoc
+ scans for sourcefiles.
+ o Added headermarker !**** for Fortran
+ o ROBODoc now warns if it finds something that looks like an item name but is
+ not in the list of item names. This catches spelling mistakes like NAEM,
+ IMPUTS, VALEU, etc
+ o Fixed bug 830868 no error message for misspelled options
+ o Fixed bug 767034 a slash at the end of a path confuses robodoc
+ o Fixed bug 772213 assert fires if header is not closed
+ o Added user configurable header types (section "header types"
+ in robodoc.rc file).
+ o Added lock mode (--lock). Per source file robodoc locks on the
+ first headermarker it finds and will recognize only that particular headermarker
+ for the remaining part of the file.
+ In addition it locks on the first remark marker in each header and
+ will recognize only that particular remark marker for the remaining
+ part of the header.
+ Petteri Kettunen
+ o First implementation of piping in generator.c
+
+September 2003 - V4.0.11
+ Frans Slothouber
+ o Added a table for header types. This will allow custom
+ header types.
+ o The master index table for HTML output is split up in
+ several pages. One for each header type.
+ Petteri Kettunen
+ o added --version option
+ o fixed regex in bump.pl
+
+September 2003 - V4.0.10
+ Skipped.
+
+Augustus 2003 - V4.0.9
+ Frans Slothouber
+ o Removed some obsolete functions.
+ o Added project file for MSVC++
+ o Merged patch from Brian P. Hanley:
+ + the option --rc to specify an alternate robodoc.rc file.
+ + the option --css to specify an alternate css file.
+ (The content of this file will be copied into the
+ robodoc.css file).
+
+Augustus 2003 - V4.0.8
+ Skipped.
+
+July 2003 - V4.0.7
+ Frans Slothouber
+ o Indented all source code with indent
+ o Added markers for VB
+ o Added a Perl example to the Examples directory (bug 779560)
+ o Fixed layout of the man pages. (bug 779553)
+ Petteri Kettunen
+ o signed/unsigned char changes and UTF-8 output in SGML and DBXML modes
+ o robohdrs/-t option, macro (#define) created as `d' type header
+
+July 2003 - V4.0.6
+ David White
+ o Added a makefile for Borland C compiler
+ Frans Slothouber
+ o Fix for bug 761686, space in .rc file confuses robodoc
+ o Added a section on how to use robodoc under win32 to
+ the manual.
+ o Fix for bug 770251, Absolute filename causes an assertion to fail
+ Kai
+ o Added a makefile for djgpp GCC under win32.
+
+July 2003 - V4.0.5
+ Frans Slothouber
+ o Fix for bug 761688, rtf output goes only 4 sections deep.
+ o Added the option --tabsize n
+ o Lines that do not start with a remark marker are skipped,
+ unless they are part of a source item.
+ o Re-enabled ascii output.
+
+ Kai Hofmann
+ o Fix for many compiler warnings.
+
+June 2003 - V4.0.4
+ Frans Slothouber
+ o Merged changes to the html generator from Kai.
+
+June 2003 - V4.0.3
+ Frans Slothouber
+ o Fix for bug -- ./configure crashes.
+ o Fix for bug -- Wrong version number is shown when compiled without using
+ autoconfig.
+
+ Jacco van Weert
+ o Added image: tag to html output.
+
+May 2003 - V4.0.2
+ o Fix for bug -- robodoc chokes on empty lines in
+ the robodoc.rc file.
+ o Fix for bug -- Bad formatting of the 'unknown section' error.
+ o Fix for bug 721685 -- TOC appears twice in a RTF Doc
+ o Fix for bug 721690 -- TOC in mutidoc files are wrong.
+ o Added support for DCL.
+
+April 2003 - V4.0.1
+ o Fix for bug 715778 -- on some machines the d_type of the
+ struct dirent is always DT_UNKNOWN. This caused ROBODoc
+ to skip all files. (Thanks to Je'rome Laheurte).
+ o Merged a patch by Andreas Signer that add a number of
+ new headr types.
+ o Merged a patch by Janusz Piwowarski that allows users
+ to specify (1) the extension of the output file and
+ (2) html character encoding.
+
+April 2003 - V4.0.0
+ o Support for XML DocBook output.
+ o A new tool, robohdrs, is included -- it can insert empty headers
+ in the source code.
+ o Support for cascading style sheets (CSS) in HTML mode.
+ o Complete rework of the commandline interface.
+ o ROBODoc can now work with directories and subdirectories,
+ and does not need xref files anymore.
+ o All documentation can be created with a single command, you
+ no longer need a makefile to create your documentation.
+ o The manual is now in docbook format and much more extensive.
+ o Lots of refactoring, code should be easier to extend and understand
+ now.
+ o There is now some design documentation.
+ o Support for single file and multiple file documentation.
+ o Dropped support for Amiga Guide format.
+ o More unittests.
+ o Support for packaging under RedHat and OS-X
+ o C++ and C examples have been removed.
+
+July 2002 - V3.2.4
+ o Indented headers
+ o 2 character header types
+ Modifications by Nathan Prewitt
+ o To allow further classification
+ of internal headers as internal functions, internal classes,
+ internal variables, etc.
+ o Added export into TROFF format with man macros -
+ suitable for Unix man pages.
+ o Fixed HTML generation to skip table of contents
+ if there were no TOC requested at command line.
+ o Fixed analyzer to correctly handle empty headers.
+
+March 2002 - Frans Slothouber (V3.2.4Beta2)
+ o More unittests
+ o Documentation update
+ o Added automatic detection of file:/
+ o Merged back the changes by Sasha Vasko.after I had
+ accidently deleted them when merging the DocBook changes.
+ o Indented most of the sourcecode.
+
+March 2002 - Frans Slothouber (V3.2.4Beta1)
+ o Documentation updates.
+ o added a target docclean in the makefile in Source/ to clean
+ all temporary files.
+ o updated documentation
+ o added testframework
+
+August 2001 DCD Changes - V3.2.4 with docbook support:
+ o Added support for Docbook SGML - Robodoc can now produce
+ output in Docbook SGML using the DBSGML command line switch,
+ allowing standardized conversion to a host of formats
+ including HTML (single and multiple), RTF, ASCII, LATEX,
+ and PDF. Docbook also
+ allows you to use external style sheets in which you can
+ change the look of your document. See www.docbook.org for more
+ on DocBook
+ o Added -rh switch which causes Robodoc to scan for a single header
+ defined as *ROBODOC* (char *robo_header) instead of header_markers
+ and uses *ROBODOC_END* as the end marker.
+ This prevents problems with Robodoc becoming confused with
+ normal comments uing *. It is particularly helpful in scanning
+ PHP source. It will only work with languages that support
+ multiline c-style comments
+ o Added -nt switch. Only used in DBSGML mode when creating a master
+ index. This causes Robodoc to list the Master Index functions as
+ a section 3 level item rather than as part of a table.
+ o Added genrd, a Unix bash shell script which simplifies the use
+ of Robodoc when generating documents from multiple source files
+ and when creating crossreferences and a master index.
+ Whole directories and subdirectories of source code can be
+ scanned and documented with a one line statement rather than
+ having to construct a makefile or using multiple
+ robodoc statements. See genrd -h for more info.
+
+Sepetember 2000 - Frans Slothouber (V3.2.3)
+ o Added a descrip.mms file for compilation under VMS
+ o make install installs additional documentation.
+ o Fixed a bug that caused links of the type
+ "someword/anotherword," to be ignored, while
+ "someword/anotherword" was recognized.
+
+July 2000 - Frans Slothouber (V3.2.2)
+ o Documentation explains how to use ROBODoc when your sources are
+ in several subdirectories.
+ o Documentation explains master index file for LaTeX, how to view
+ the generated documentation, and the new options, NOSOURCE, SINGLEDOC
+ and TITLE.
+ o example makefile includes commands to view the
+ generated documentation.
+ o Updated the man page
+ o Added option SINGLEDOC
+ For LaTeX output this generates documentation without
+ the start and end headers. This way the generated file
+ can be included in a master file.
+ o Added master index file for LaTeX output. The documentation
+ gathered from several source files can now be included into
+ one big file.
+ o Added the option NOSOURCE. With this option the SOURCE item
+ is not included in the documentation.
+ o Added the TITLE option. This allows to set the title for
+ the master index file.
+ o Made the search for headermarkers case insensitve.
+ REM == Rem == rem
+
+December 1999 - v3.1f
+ o added RB_TimeStamp() to include time stamps in the documentation.
+ o Documentation is now generated in LaTeX2e format.
+ o added '|****' as begin marker, '|' as remark marker and '|***' as
+ end marker for GNU assembler support.
+ o ran ident on all source. Using the GNU standard now.
+ o Added new fold markers provided by Petteri
+
+December 1999 - Frans Slothouber (v3.1e)
+ o Moved the C example in Examples to Examples/C
+ o Added an C++ example in Examples/CPP
+ o Added empty headers for C++ in Headers/
+ o More documentation.
+ o added markers for HTML.
+ o modified the RB_Find_Link() function to also words that include
+ "::". This is used for C++ methods.
+ o added a RB_Function_Name() function that correctly extracts the
+ function name (or the name of any other object that is documented)
+ from the header name. The old code used RB_FilePart which failed
+ on C++ method names.
+ o Fixed a core-dumping bug in RB_Set_Doc_Base()
+
+December 1999 - Frans Slothouber (v3.1d)
+ o Added list of possible item names to the robodoc man page.
+ o Added list of possible header types to the robodoc man page.
+ o Updated manual with information on the generation of the
+ master index file and new header types.
+ o added new header types for, classes, methods, variables,
+ functions, strutures and constants. (Idea of Stefan Kost)
+ o added a command to create a master index file that contains
+ sorted pointers to all classes, methods, variables,
+ functions, strutures and constants.
+
+December 1999 - Frans Slothouber (v3.1c)
+ o Added testheader.c for debug purposes.
+ o Split the source code into serveral files.
+ o Fixed numerous typos in the documentation.
+ o Using m4 to create the html documentation (for table of contents ect).
+ o Added cross links between the documentation and examples.
+ From patches that I received from Stefan Kost
+ o renamed BEAST METHODS -> METHODS
+ o renamed BEAST ATTRIBUTES -> ATTRIBUTES
+ o added new items useful for object oriented programming; some of
+ these items are already used in os3.1 autodocs
+ TAGS, COMMANDS, DERIVED FROM, DERIVED BY, USES,
+ CHILDREN, USED BY, PARENTS, USAGE, PURPOSE
+ o commented the item names
+ o changed item-type enums to end all with _ITEM
+ o changed RB_Find_Link to accept names ending with '...'
+ o changed copyright comment to be a style-guide conform version string.
+ o changed RB_VER[] to be a style-guide conform version string
+ o changed AMIGA into _AMIGA, because the first one does not exists,
+ when compiling with NOANSI on SAS C/C++
+
+November 1999 - Frans Slothouber (v3.1b)
+ o Added a man page
+ o Cleaned-up html documentation.
+ o <A NAME="source code file name"> is generated at the beginning of
+ each document. A mention of the source code name in another
+ document creates a link to this name (provided you use xrefs).
+ o Moved most #defines and enums to robodoc.h
+ o Made ROBODoc more forgiving in reading the xrefs file. Empty
+ lines are allowed and also spaces at the end of a file name.
+
+August 1999 - Frans Slothouber: v3.1
+ o Added GPL licence
+ o Added INSTALL, README, and TODO
+ o Converted the documentation to HTML
+ o Spell-checked all documentation
+ o More documentation and a more informative usage() function.
+ o robodoc -c prints licence
+ o removed a number of Source items from the documentation to reduce
+ the size of the robodoc.c.html file... no fun for people
+ to download a >100k file.
+ o removed the warning about not using a robodoc default file.
+ o indent -orig -i2 -nbc -ncdb -bad -bap
+ o Fixed the warnings.
+ o Fixed some occurrences of (evil cast)malloc (thou shalt not
+ cast malloc :)
+ o ROBODoc now returns EXIT_FAILURE or EXIT_SUCCESS, as defined
+ in <stdlib.h>
+ o Fixed a memory leak in RB_Analyse_Document()
+
+Modifications by Petteri Kettunen
+August 1999 - v3.0m+
+ o Support for folding in SOURCE items, HTML only.
+ o indent -kr
+ o Added options FOLD and C
+
+Modifications by Petteri Kettunen
+Feb 1999 - v3.0m
+ o Changed background color to white
+ o Changed size of Table of Contents title. (H3 instead of H1)
+ o The reverse function also reversed the sorted header list,
+ fixed this.
+
+Modifications by FNC Slothouber.
+ Feb-1999 - v3.0l * Added function to reverse the header list.
+14-Aug-1998 - v3.0k * Tcl/Tk '#' handling added;
+
+
+Modifications by agi
+15-Dec-1997 - v3.0j
+ o cleaned the HTML-output, so it now conforms to the DTD for HTML-3.2
+ o TOC now is an ordered list (<OL> and <LI>)
+ o added "<!DOCTYPE..>"
+ o added quotes to values of some HTML-attributes
+ o more compatible implementation of the SGML-comment containing
+ copyright-info replaced all occurrences of <B><PRE>.. by <PRE><B>
+ o replaced <H2/3> by <H1/2>
+ o fixed two minor warnings reported by gcc -Wall
+
+Modifications by FNC Slothouber.
+10-July-1996 - v3.0i
+ o Bug Fix, Both the options INTERNAL and INTERNALONLY did not
+ work correctly.
+01-April-1996 - v3.0h
+ o Added ';' to > and < so lynx also recognizes them.
+ o Fancied up the HTML output.
+
+Modifications by apang
+ 08-Mar-1996 - v3.0f
+ o Cleaner build for Borland C++ 4.52
+ o Added more markers (C++, Pascal, Modula-2, COBOL)
+ o Added more item types/names
+ o Added #defines for the preamble (COMMENT_ROBODOC and
+ COMMENT_COPYRIGHT)
+ o BLANK_HEADER for detection of asterisk'd lines
+ o RB_Say() the GENERIC header warning instead of using printf()
+ o Indents SOURCE body in output
+ o ASCII respects the TOC flag; removed extraneous newline after
+ formfeed (so it's more like AutoDoc)
+ o HTML output fixed to handle '<', '>', and '&'
+ o LaTeX attributes and '%' handling added; fancied up the output a bit
+ o RTF support added
+ o Changed some fprintf()'s to fputc()'s for potentially lower overhead
+ o Fixed line eater bug
+ o More general fix to the TOC problem of including internal links
+ when it wasn't selected
+
+Modifications by FNC Slothouber.
+04-Feb-1996 - v3.0e
+ o fixed the problem with the TOC that included links to headers that
+ were not selected. (i.e internal)
+08-Oct-1995 - v3.0d
+ o Bug fixes
+16-Sep-1995 - v3.0c
+ o Bug fixes
+27-Aug-1995 - v3.0b
+ o Fixed a bug with the defaults file
+ o Improved search algorithm RoboDoc is now 5.8 times faster.
+18-Aug-1995 - v3.0
+ o New scanner that searches for a set default markers
+ that define what is a comment or what is not and that
+ define what or what is not a header/end marker.
+ o Added Beast Support
+
+08-Aug-1995 - Koessi -
+ o a lot of while instead of for
+ o a lot of switch() instead of ifelse
+ o version defined
+ o RB_Say, RB_Panic now useable like printf()
+ o new formats for nearly all output-strings
+ o char *whoami is global copy of argv[0]
+ o BOLD <- MAKE_LARGE && AMIGAGUIDE
+ o succesfully compiled&tested on HPUX (HP9000/800)
+ o optimized listfunctions
+ o encapsulated header- and link- allocating and freeing
+ o RB_Find_Function_Name() replaced with RB_FilePart()
+
+07-Aug-1995 - Koessi -
+ o automated foldmarks "\***"
+ o ! GoldEd's foldmarks == RoboDoc marker !
+ o quoted source parsing enhanced
+
+01-Aug-1995 - Koessi - v2.0?
+ o more robust parsing, less enforcer-hits
+ o removed self-referencing links !
+ o remarked most changes with *koessi*
+ o added GoldEd-foldmarks
+ o compiled successfully with SAS-C 6.3
+
+24-May-1995 - Frans Slothouber - v2.0e
+ o Fixed a bug that cause the CleanUp Routine to lock up.
+ o Improved the HTML output, should work faster now.
+
+15-May-1995 - Frans Slothouber - v2.0d
+ o New Defaults file.
+ o Added Verbose option.
+
+12-May-1995 - Frans Slothouber - v2.0c
+ o Bug fixes.
+
+10-May-1995 - Frans Slothouber - v2.0a
+ o Program completely rewritten
+ o added SOURCE item and LaTeX output.
+ o added TAB converter.
+
+20-Apr-95 - Jacco van Weert - v1.1a:
+ o INTERNALONLY option added.
+ o Sort problem solved.
+
+2-Apr-95 - Jacco van Weert - v1.0b: Bug fixes
+ o Procedure header search bug solved.
+ o Print 'created procedure' text
+
+Mar-95 - Jacco van Weert - v1.0a: Final version
+
+2-Feb-95 - Jacco van Weert - v0.93:
+ o Mungwall hit, solved.
+ o When item headers, are also available in body then parts are
+ duplicated solved.
+
+26-Jan-95 - v0.92: 2nd test beta-version
+
+19-January-95 - Jacco van Weert (v0.8)
+ o First test beta-version
+
+December 1994 - Jacco van Weert -- creation date.
+
+
--- /dev/null
+#!/usr/bin/env kaptain
+# $Id: robodoc.kaptn,v 1.3 2007/01/29 22:28:20 gumpu Exp $
+# NAME
+# robodoc.kaptn - robodoc.rc gui composer
+# COPYRIGHT
+# Copyright (C) 2003 gian paolo ciceri <gp.ciceri@acm.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 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+#
+# HISTORY
+# Sep, 9th 2003: v0.1.0 - initial, tentative release (borrowed from procmail.kaptn).
+#
+
+
+#############################################################
+#
+# main wizard
+#
+start :wizard "Robodoc Configuration Wizard" -> intro setup finish;
+#############################################################
+
+#############################################################
+#
+# into page
+#
+intro "robodoc.rc builder" -> intro1 intro2;
+intro1 -> text1 introa;
+text1 -> @text("This is a graphical front-end for creating robodoc.rc \
+configuration files made using Kaptain, the Universal graphical front-end.");
+intro2 "" -> @;
+introa "" -> workdir @fill;
+workdir "Choose Working Directory" -> "\nWORKDIR=" @directory="$PWD";
+#############################################################
+
+#############################################################
+#
+# finish page, where I write the robodoc.rc file
+#
+finish "Finish" -> text3 other_settings;
+text3 -> @text("Now press the Preview button to see the settings you have made.\
+ Don't forget to press Write button in order to save the setting in the given file.");
+other_settings -> filename @fill @preview(allfile)="Preview" @action(writetofile)="Write";
+writetofile-> "echo '" allfile "' > " filename ;
+filename -> @outfile="$PWD/robodoc.rc";
+#############################################################
+
+#############################################################
+#
+# setup page, here's the beef!
+#
+setup :horizontal "Settings" -> recipe;
+recipe :framed :horizontal -> recipe1 recipe2;
+recipe1 :framed "Main documentation options" -> outputtype sourcedir docdir cssfile ;
+recipe2 :framed "Items and misc. options" -> items ignoreitems ignoreext flags;
+#recipe :framed -> outputtype sourcedir docdir cssfile items ignoreitems ignoreext flags regexp;
+
+outputtype :horizontal "Output Types" -> !these_output | more_output ;
+ these_output :beside -> "--html\n" @string="--html";
+ more_output :beside -> "\n" more_output0;
+
+sourcedir "Sources Directory" -> "--src\n" @directory="$PWD";
+
+docdir "Documents Directory" -> "--doc\n" @directory="$PWD";
+
+cssfile "CSS stylesheet" -> "--css\n" @infile("*.css");
+
+items :horizontal "Items" -> !these_items | more_items;
+ these_items :beside -> @string;
+ more_items :beside -> more_items0;
+
+ignoreitems :horizontal "Ignore Items" -> !these_iitems | more_iitems;
+ these_iitems :beside -> @string;
+ more_iitems :beside -> more_iitems0;
+
+ignoreext :horizontal "Ignore file Extensions" -> these_iexts | !more_iexts;
+ these_iexts :beside -> @string="*.bkp\n ~";
+ more_iexts :beside -> more_iexts0;
+
+###########################################################
+#@list('NAME', 'SYNOPSIS', 'INPUTS', 'OUTPUTS', 'HISTORY') ;
+#ignoreitems "Ignore Items" -> "ignore items:\n" @combow('HISTORY', 'BUGS', 'AUTHORS', 'TODO', 'NEWS');
+#ignoreext "Ignore Extensions" -> "extensions:\n" @combow('*.bkp', '~');
+###########################################################
+
+
+
+flags :horizontal "Misc.Options" -> these_flags | !more_flags ;
+ these_flags :beside -> ":0 " @string;
+ more_flags :beside -> ":0 " more_flags0;
+#############################################################
+
+
+#############################################################
+#
+# robodoc.rc file setup, in the `allfile` variable
+#
+allfile -> "#This file was created by Kaptain\n\n"
+ "#options" "\n"
+ options "\n"
+
+ "#output types" "\n"
+ out_options "\n"
+
+ "#source directory" "\n"
+ sourcedir "\n"
+
+ "#document directory" "\n"
+ docdir "\n"
+
+ "#stylesheet" "\n"
+ cssfile "\n"
+
+ "#items" "\n"
+ "items" "\n"
+ items "\n"
+
+ "#ignore items" "\n"
+ "ignore items:\n"
+ ignoreitems "\n"
+
+ "#ignore files by extension" "\n"
+ "extensions:" "\n"
+ ignoreext "\n"
+
+ "#end\n" ;
+#############################################################
+
+
+#############################################################
+#
+# auxiliary dialog, to setup options
+#
+more_flags0 :dialog "Options..." -> flags_top flags_buttons;
+flags_buttons -> @close="OK";
+flags_top :horizontal :framed -> options;# extras;
+# extras "Extras" -> @;
+ options "Options"
+ -> index multidoc singledoc singlefile
+ tell folds internal internalonly
+ nosource nodesc sections tabsize toc;
+
+ index "Also create a master index file" -> "--index\n"! | @;
+ multidoc "Generate one document per source file, and copy the directory hierarchys" ->
+ "--multidoc\n"! | @;
+ singledoc "Define the documentation directory or documentation file." -> "--singledoc\n" |!@;
+ singlefile "Generate a single document from a single file." -> "--singlefile\n" |!@;
+ tell "ROBODoc tells you what steps it is taking." -> "--tell\n" |!@;
+ folds "Use fold marks to split a big document into smaller ones." -> "--folds\n" |!@;
+ internal "Also include headers marked internal." -> "--internal\n" |!@;
+ internalonly "Only include headers marked internal." -> "--internalonly\n" |!@;
+ nosource "Do not include the SOURCE items." -> "--nosource\n" |!@;
+ nodesc "Do not scan any subdirectories." -> "--nodesc\n" |!@;
+ sections "Create sections based on the module hierarchy." -> "--sections\n" |!@;
+ tabsize "Lets you specify the tabsize." -> "--tabsize\n" |!@;
+ toc "Add a table of contents." -> "--toc\n" |!@;
+#############################################################
+
+#############################################################
+#
+# auxiliary dialog, to setup output types (html, latex, rtf, docbook)
+#
+more_output0 :dialog "Output Types..." -> output_top output_buttons;
+output_buttons -> @close="OK";
+output_top :horizontal :framed -> out_options;# extras;
+# extras "Extras" -> @;
+ out_options "Output Types"
+ -> html ascii rtf latex docbook;
+ html "HTML" -> "--html\n"! | @;
+ ascii "ASCII" -> "--ascii\n"! | @;
+ latex "LaTeX" -> "--latex\n" | !@;
+ rtf "Rich Text format" -> "--rtf\n" | !@;
+ docbook "DocBook" -> "--dbxml\n" | !@;
+#############################################################
+
+#############################################################
+#
+# auxiliary dialog, to setup items
+#
+more_items0 :dialog "Include Items..." -> item_top item_buttons ;
+item_buttons -> @close="OK";
+item_top :horizontal :framed -> item_options;# extras;
+# extras "Extras" -> @;
+item_options :horizontal "Items"
+ -> itemleft itemright;
+
+ itemleft -> NAME SYNOPSIS INPUTS
+ USAGE SOURCE COPYRIGHT FUNCTION AUTHOR RESULT EXAMPLE NOTES DIAGNOSTICS;
+
+ itemright -> WARNINGS ERRORS BUGS SEEALSO METHODS ATTRIBUTES TAGS COMMAND DERIVEDFROM
+ DERIVEDBY USES USEDBY INPUTS OUTPUTS HISTORY;
+
+ NAME "NAME, Item name + short description" -> " NAME\n"! | @;
+ SYNOPSIS "SYNOPSIS, how to use it" -> " SYNOPSIS\n" | !@;
+ USAGE "USAGE, how to use it" -> " USAGE\n" | !@;
+ SOURCE "SOURCE, source code inclusion" -> " SOURCE\n" | !@;
+ COPYRIGHT "COPYRIGHT, who own the copyright" -> " COPYRIGHT\n" | !@;
+ FUNCTION "FUNCTION, what does it" -> " FUNCTION\n" | !@;
+ AUTHOR "AUTHOR, who wrote it" -> " AUTHOR\n" | !@;
+ RESULT "RESULT, what do we get returned" -> " RESULT\n" | !@;
+ EXAMPLE "EXAMPLE, a clear example of the items use" -> " EXAMPLE\n" | !@;
+ NOTES "NOTES, any annotations" -> " NOTES\n" | !@;
+ DIAGNOSTICS "DIAGNOSTICS, diagnostical output" -> " DIAGNOSTICS\n" | !@;
+ WARNINGS "WARNINGS, warning messages" -> " WARNINGS\n" | !@;
+ ERRORS "ERRORS, error messages" -> " ERRORS\n" | !@;
+ BUGS "BUGS, known bugs" -> " BUGS\n" | !@;
+ SEEALSO "SEE ALSO, references" -> " SEE ALSO\n" | !@;
+ METHODS "METHODS, oop attributes" -> " METHODS\n" | !@;
+ ATTRIBUTES "ATTRIBUTES, oop attributes" -> " ATTRIBUTES\n" | !@;
+ TAGS "TAGS, tagitem description" -> " TAGS\n" | !@;
+ COMMAND "COMMAND, command description" -> " COMMAND\n" | !@;
+ DERIVEDFROM "DERIVED FROM, oop super class" -> " DERIVED FROM\n" | !@;
+ DERIVEDBY "DERIVED BY, oop sub class" -> " DERIVED BY\n" | !@;
+ USES "USES, what modules are used by this one" -> " USES\n" | !@;
+ USEDBY "USED BY,which modules do use this" -> " USED BY\n" | !@;
+ INPUTS "INPUTS, what can we feed into it" -> " INPUTS\n" | !@;
+ OUTPUTS "OUTPUTS, what output will be made" -> " OUTPUTS\n" | !@;
+ HISTORY "HISTORY, who done what changes when" -> " HISTORY\n" | !@;
+#############################################################
+
+#############################################################
+#
+# auxiliary dialog, to setup ignore items
+#
+more_iitems0 :dialog "Ignore Items..." -> iitem_top iitem_buttons;
+iitem_buttons -> @close="OK";
+iitem_top :horizontal :framed -> iitem_options;# extras;
+# extras "Extras" -> @;
+ iitem_options :horizontal "Ignore Items"
+ -> iitemleft iitemright;
+
+ iitemleft -> iNAME iSYNOPSIS iINPUTS
+ iUSAGE iSOURCE iCOPYRIGHT iFUNCTION iAUTHOR iRESULT iEXAMPLE iNOTES iDIAGNOSTICS;
+
+ iitemright -> iWARNINGS iERRORS iBUGS iSEEALSO iMETHODS iATTRIBUTES iTAGS iCOMMAND iDERIVEDFROM
+ iDERIVEDBY iUSES iUSEDBY iOUTPUTS iHISTORY iBUGS;
+
+ iNAME "NAME, Item name + short description" -> " NAME\n" | !@;
+ iSYNOPSIS "SYNOPSIS, how to use it" -> " SYNOPSIS\n" | !@;
+ iUSAGE "USAGE, how to use it" -> " USAGE\n" | !@;
+ iSOURCE "SOURCE, source code inclusion" -> " SOURCE\n" | !@;
+ iCOPYRIGHT "COPYRIGHT, who own the copyright" -> " COPYRIGHT\n" | !@;
+ iFUNCTION "FUNCTION, what does it" -> " FUNCTION\n" | !@;
+ iAUTHOR "AUTHOR, who wrote it" -> " AUTHOR\n" | !@;
+ iRESULT "RESULT, what do we get returned" -> " RESULT\n" | !@;
+ iEXAMPLE "EXAMPLE, a clear example of the items use" -> " EXAMPLE\n" | !@;
+ iNOTES "NOTES, any annotations" -> " NOTES\n" | !@;
+ iDIAGNOSTICS "DIAGNOSTICS, diagnostical output" -> " DIAGNOSTICS\n" | !@;
+ iWARNINGS "WARNINGS, warning messages" -> " WARNINGS\n" | !@;
+ iERRORS "ERRORS, error messages" -> " ERRORS\n" | !@;
+ iBUGS "BUGS, known bugs" -> " BUGS\n" | !@;
+ iSEEALSO "SEE ALSO, references" -> " SEE ALSO\n" | !@;
+ iMETHODS "METHODS, oop attributes" -> " METHODS\n" | !@;
+ iATTRIBUTES "ATTRIBUTES, oop attributes" -> " ATTRIBUTES\n" | !@;
+ iTAGS "TAGS, tagitem description" -> " TAGS\n" | !@;
+ iCOMMAND "COMMAND, command description" -> " COMMAND\n" | !@;
+ iDERIVEDFROM "DERIVED FROM, oop super class" -> " DERIVED FROM\n" | !@;
+ iDERIVEDBY "DERIVED BY, oop sub class" -> " DERIVED BY\n" | !@;
+ iUSES "USES, what modules are used by this one" -> " USES\n" | !@;
+ iUSEDBY "USED BY,which modules do use this" -> " USED BY\n" | !@;
+ iINPUTS "INPUTS, what can we feed into it" -> " INPUTS\n" | !@;
+ iOUTPUTS "OUTPUTS, what output will be made" -> " OUTPUTS\n" | !@;
+ iHISTORY "HISTORY, who done what changes when" -> " HISTORY\n" | !@;
+#############################################################
+
+#############################################################
+#
+# auxiliary dialog, to setup excluded file by extension
+#
+more_iexts0 :dialog "Exclude Extensions..." -> iextm_top iextm_buttons;
+iextm_buttons -> @close="OK";
+iextm_top :horizontal :framed -> iextm_options;# extras;
+# extras "Extras" -> @;
+ iextm_options "Exclude files by extension"
+ -> BAK TILDE;
+ BAK "*.bak" -> " *.bak\n" | @;
+ TILDE "~" -> " ~\n" | @;
+#############################################################
+
--- /dev/null
+#!/usr/bin/perl
+###################################################
+# robodoc 2 pod converter
+###################################################
+#****h* robodoc2pod
+# NAME
+# Robodoc 2 Pod
+#
+# FUNCTION
+# Generate POD documentation from ROBODoc to allow
+# use of perldoc with your Robodoc'ed code.
+#
+# HISTORY
+# * V 0.2.1 of 06/03/14 corrected the regexps
+# * V 0.2.0 of 06/03/13 rewritten with intermediate representation
+# * V 0.1.0 of 06/03/10 first version
+#
+# BUGS
+# nothing known right now.
+#
+# TODO
+# * refactor cleanly
+# * manage locales
+# * manage nested lists
+# * indent EXAMPLE with
+#
+# LICENSE
+# This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+# AUTHOR
+# Emmanuel Florac ( wazoox @ free . fr )
+# COPYRIGHT
+# (c) 2006 Intellique (www.intellique.com)
+#***
+# always use strict et warnings.
+use strict;
+use warnings;
+
+use Data::Dumper;
+
+#########################
+# functions
+#########################
+
+sub usage {
+ return "usage : $0 <source file> [ >> <pod file> ]";
+}
+
+#########################
+# main
+#########################
+
+# must provide a file name to work with
+my $file = shift or die usage();
+open my $fh, $file or die "can't open file : $file";
+
+# robodoc start and end tags (marks robodoc blocks)
+my $rbd_starttag = qr(^\*\*\*\*[\w\*]\*);
+my $rbdheadtype = qr(^\*\*\*\*([\w\*])\*);
+my $rbd_endtag = qr(^\*\*\*);
+
+# robodoc general tags
+my @rbdtags = (
+ 'NAME', 'COPYRIGHT', 'SYNOPSIS', 'USAGE',
+ 'FUNCTION', 'DESCRIPTION', 'PURPOSE', 'AUTHOR',
+ 'CREATION DATE', 'MODIFICATION', 'HISTORY', 'INPUTS',
+ 'ARGUMENTS', 'OPTIONS', 'PARAMETERS', 'SWITCHES',
+ 'OUTPUT', 'SIDE EFFECTS', 'RESULT', 'RETURN VALUE',
+ 'EXAMPLE', 'NOTES', 'DIAGNOSTICS', 'WARNINGS',
+ 'ERRORS', 'BUGS', 'TODO', 'IDEAS',
+ 'PORTABILITY', 'SEE ALSO', 'METHODS', 'NEW METHODS',
+ 'ATTRIBUTES', 'NEW ATTRIBUTES', 'TAGS', 'COMMANDS',
+ 'DERIVED FROM', 'DERIVED BY', 'USES', 'CHILDREN',
+ 'USED BY', 'PARENTS', 'SOURCE', 'LICENSE',
+);
+
+my %rbdheaders = (
+ c => 'Class',
+ d => 'Constant',
+ f => 'Fonction',
+ h => 'Module',
+ m => 'Méthod',
+ s => 'Structure',
+ t => 'Type',
+ u => 'Unit Test',
+ v => 'Variable',
+ '*' => '',
+);
+
+# to check for headers tags
+my $tagmatch = join '|', @rbdtags;
+$tagmatch = qr(^($tagmatch));
+
+# to store the robodoc
+my @robodoc;
+
+# flag and titles
+my $inrobodoc = 0;
+my $rbdtagname = '';
+
+# read the file
+while (<$fh>) {
+
+ # remove leading # if any
+ s/^\s*# *//;
+ chomp;
+
+ $inrobodoc = 0 if m/$rbd_endtag/;
+
+ if ($inrobodoc) {
+ push @{ $robodoc[$#robodoc]{$rbdtagname} }, $_;
+ }
+
+ if (m/$rbd_starttag/) {
+ $inrobodoc = 1;
+ my ($headertype) = (m/$rbdheadtype/);
+ ($rbdtagname) = (m/$rbd_starttag(.*)/);
+ chomp $rbdtagname;
+ if ($rbdtagname) {
+ $rbdtagname = $rbdheaders{$headertype} . $rbdtagname;
+ push @robodoc, { $rbdtagname => [] };
+ }
+ }
+}
+
+close $fh;
+
+# now convert robodoc to pod
+my @pod;
+my $items = 0;
+my $podhead = 1;
+
+foreach (@robodoc) {
+ my ( $k, $v ) = each %$_;
+ my $currhead = $podhead;
+ push @pod, '', "=head$currhead $k", '';
+ $currhead++;
+
+ foreach my $line (@$v) {
+ # insert head if this is a managed tag
+ if ( $line =~ m/$tagmatch/ ) {
+ push @pod, ( '', "=head$currhead $line", '' );
+ # insert bulleted lists
+ } elsif ( my ($elem) = ( $line =~ m/^\*\s+(.*)/ ) ) {
+ if ( $items == 0 ) {
+ $items++;
+ push @pod, "=over";
+ }
+ push @pod, ( '', '=item *', '', $elem );
+ # end bulleted list
+ } elsif ( $items > 0 ) {
+ $items = 0;
+ push @pod, ('', '=back', '');
+ push @pod, $line;
+ # raw insert
+ } else {
+ push @pod, $line;
+ }
+ }
+}
+
+print join( "\n", @pod ) . "\n";
+
--- /dev/null
+$Id: DEVELOPERS,v 1.10 2006/06/17 11:35:10 gumpu Exp $
+
+If you get this from CVS, this is how to create a workable archive.
+Run:
+ aclocal
+ automake -a
+ autoconf
+
+
+To create a new release edit
+ bump.pl
+and then run it. Then run
+ ./do.sh
+
+
+To clean _everything_
+
+ make -f makefile.am myclean
+
+
+To create the (g)zipfiles:
+ ./configure
+ make dist
+ make dist-zip
+
+
+To check for memory leaks use:
+ CFLAGS="-g -Wall -DDMALLOC -DMALLOC_FUNC_CHECK" LDFLAGS="-ldmalloc" ./configure
+in combination with the dmalloc library ( http://dmalloc.com/ )
+
+
+To cross-compile Windows binary on GNU/Linux, do:
+
+ (cd Source && make -f makefile.plain xcompile)
+
+This requires mingw32 cross-compiler. On Debian GNU/Linux (and on its
+derivatives) this can be installed simply by typing:
+
+ apt-get update && apt-get install mingw32
+
+
+There are three modules for ROBODoc
+robo -- The source code and documentation
+robotest -- A test suite ( OBSOLETE )
+robowwww -- The web pages for ROBODoc. ( OBSOLETE )
+
--- /dev/null
+
+Some random notes about the design of robodoc.
+This will later be turned into a proper docbook
+document.
+
+
+
+Collections / Containers.
+
+For most of the containers a singly linked list is used.
+The link to the next element is called "next" and is the
+first field in any structure that is stored in a linked list.
+
+For instance
+
+struct S {
+ struct S* next;
+ long another_field;
+ char* aName;
+};
+
+An anchor points to the first element in the list.
+
+If this convention is used for all linked lists than it is
+easy to remember how to iterate through the container.
+
+ struct X* x_container;
+
+ struct X* x_iterator;
+ for ( x_iterator = x_container;
+ x_iterator != 0;
+ x_iterator = x_iterator->next ) {
+
+ }
+
+Inserting an element:
+
+ x_new_element->next = x_container;
+ x_container = x_new_element;
+
+
+========================================================
+
+General Flow
+
+Sourcecode ---> [ROBODoc] ---> Document.
+
+The sourcecode consists of one or more sourcefiles.
+The resulting document consists of one or more parts.
+
+For every sourcefile that is one part.
+There can be additional parts. For instance
+for indexfiles or to pull all parts into a single document.
+
+
+> Can someone explain the differences internally of a single doc
+> and multi doc? At first glance, it would seem that Lua scripts
+> should only implement single doc because they are to be free to
+> do as they please, but I may be mistaken.
+>
+> Please advise.
+
+The difference between singledoc and multidoc mode.
+
+The whole robodoc process consists of step 3
+(1) The scanning of the source directory tree -- this collect the names of
+ all the files and directories in the source directory tree.
+ All this information is stored in a RB_Directory structure inside
+ a RB_Document structure.
+(2) The analysing of all the sourcefiles -- this reads the content of
+ all the files found in step 1 and collects all the headers found in
+ these files.
+ These are store in list of RB_Part structures stored inside a
+ RB_Document structure. For each sourcefile there is a RB_Part
+ structure. All the headers found in a single source file are
+ stored as a list of RB_header structures in a RB_Part structure.
+(3) the generation of the documentation -- write the headers a
+ single documentation file or mulitiple documentation files.
+
+
+The difference between single doc and multidoc can be found in step 3.
+The difference can be seen in most clearly in the functions:
+RB_Generate_SingleDoc() and RB_Generate_MultiDoc()
+
+If you look at the start of both functions they both call:
+
+ RB_Document_Collect_Headers( document );
+ RB_Document_Link_Headers( document );
+ RB_Fill_Header_Filename( document );
+ RB_Name_Headers( document->headers, document->no_headers );
+ RB_CollectLinks( document, document->headers, document->no_headers );
+
+However before this RB_Generate_MultiDoc() calls
+
+ RB_Document_Determine_DocFilePaths( document );
+ RB_Document_Create_DocFilePaths( document );
+ RB_Document_Determine_DocFileNames( document );
+
+This creates the documentation directory tree, which is a mirror image
+of the source directory tree, and determines for each RB_Part
+(sourcefile) the filename for the documentation file.
+The information is later used in RB_Fill_Header_Filename().
+
+RB_Fill_Header_Filename() stores the name of the file a header is
+to be written to. In single doc mode RB_Fill_Header_Filename() this
+filename is always the same (the filename specified with the
+--doc option). In multidoc mode the filename can be different and is
+based on the name computed in RB_Document_Determine_DocFileNames().
+
+In singledoc mode a single file is now opened. For each part,
+the headers in this part are written to this file. (So all
+headers end up in the same file).
+
+In multidoc mode a new file is opened for each part, and the headers
+in the part are written to this file. (So headers end up in
+different files).
+
+In addition in multidoc mode a series of index files can be generated.
+
+
+
+=====================================================================
+
+General Flow
+
+Sourcecode ---> [ROBODoc] ---> Document.
+
+The whole ROBODoc process consists of three steps: scanning,
+analysing, generating.
+
+Scanning
+
+ROBODoc scans the source directory tree. This collects the names of
+all the source files.
+
+Analysing
+
+ROBODOc analyses all the sourcefiles. This reads the content of all
+source files and collects all the headers.
+
+Generating
+
+In this step the headers are written to one or more documentation files.
+In addition
+
+The data collected during scanning and analysing is stored in a
+number of structures.
+
+RB_Directory, it stores the names of the sourcefiles and directories
+in the source direcory tree. Files names are stored in a RB_Filename
+structure, directory names in a RB_Path structure. Each RB_Filename
+contains a pointer (path) a RB_Path that tells in which directory a
+file is located. Each RB_Path has a pointer (parent) to another
+RB_Path that tells of which directory is a directory located (of which
+directory it is a subdirectory). The only exception is the root
+directory.
+
+Besides the name of the sourcefile, the RB_Filename also stores the
+name of the documentation file.
+
+For each sourcefile there is an RB_Part structure. It contains a
+pointer (filename) to the RB_Filename and a list (headers) of
+RB_Header structure containing the headers found in the sourcefile.
+
+Every RB_Header structure contains a pointer (owner) to the RB_Part
+structure to which it belongs. Headers can form a hierarchy that is
+used to create sections and subsections in the documentation. To
+store this hierarchy every RB_header structure contains a pointer
+(parent) to its parent header. For instance, given the following two
+headers, SubModule is the parent of SubSubModule.
+
+/****h* TopModule/SubModule
+ *
+ ****/
+
+/****h* SubModule/SubSubModule
+ *
+ ****/
+
+In the documentation this creates the sections
+
+1.TopModule
+1.1 SubModule
+1.1.1 SubSubModule
+
+
+The RB_Directory and the linked list of RB_Part structures are
+stored in a RB_Document structure.
+
+During the generation of the documentation ROBODoc tries to create
+cross links between the mention of a header's name (on object) and the
+documentation generated from that header (the documentation for the
+object).
+
+To aid this proces there is an array of RB_link structures. This
+array is sorted for quick searching.
+
+
--- /dev/null
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 5400 8100 6900 9600
+6 5400 8100 6900 9600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5400 8100 6900 8100 6900 9600 5400 9600 5400 8100
+4 1 0 50 0 12 12 0.0000 4 180 945 6150 8400 RB_Header\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 5400 8550 6900 8550
+-6
+6 5400 8100 6900 9600
+6 5400 8100 6900 9600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5400 8100 6900 8100 6900 9600 5400 9600 5400 8100
+4 1 0 50 0 12 12 0.0000 4 180 945 6150 8400 RB_Header\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 5400 8550 6900 8550
+-6
+6 10200 900 11700 2400
+6 10200 900 11700 2400
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 10200 900 11700 900 11700 2400 10200 2400 10200 900
+4 1 0 50 0 12 12 0.0000 4 180 1260 10950 1200 RB_Directory\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 10200 1350 11700 1350
+-6
+6 3600 10500 5100 12000
+6 3600 10500 5100 12000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 10500 5100 10500 5100 12000 3600 12000 3600 10500
+4 1 0 50 0 12 12 0.0000 4 180 735 4350 10800 RB_Link\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3600 10950 5100 10950
+-6
+6 300 10500 1800 12000
+6 300 10500 1800 12000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 300 10500 1800 10500 1800 12000 300 12000 300 10500
+4 1 0 50 0 12 12 0.0000 4 135 735 1050 10800 Globals\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 300 10950 1800 10950
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1800 4500 3300 4500 3300 6000 1800 6000 1800 4500
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 5400 4500 6900 4500 6900 6000 5400 6000 5400 4500
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3300 4800 5400 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 1
+ 5100 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 6900 4800 9000 4800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 9000 4650 10500 4650
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 5400 4950 6900 4950
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 1800 4950 3300 4950
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 11700 4650 13200 4650
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 6150 6000 6150 8100
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 6150 6000 6150 8100
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 2550 6000 2550 8700 5400 8700
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 11700 1800 12600 1800 12600 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 0 0 1.00 90.00 150.00
+ 2550 4500 2550 1500 10200 1500
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 1 0 3
+ 0 0 1.00 90.00 150.00
+ 10200 1800 9600 1800 9600 4200
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 11700 4200 13200 4200 13200 5700 11700 5700 11700 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 10500 4500 11700 4500
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 9000 4200 10500 4200 10500 5700 9000 5700 9000 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 4
+ 0 0 1.00 60.00 120.00
+ 6900 9300 8250 9300 8250 8400 6900 8400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 1800 11250 3600 11250
+4 1 0 50 0 12 12 0.0000 4 165 1155 2550 4800 RB_Document\001
+4 1 0 50 0 12 12 0.0000 4 165 735 6150 4800 RB_Part\001
+4 1 0 50 0 12 12 0.0000 4 165 525 4950 4650 parts\001
+4 1 0 50 0 12 12 0.0000 4 165 1155 4500 6450 RB_Document\001
+4 1 0 50 0 12 12 0.0000 4 180 1155 8250 4650 source_file\001
+4 1 0 50 0 12 12 0.0000 4 135 735 5700 7500 headers\001
+4 1 0 50 0 12 11 0.0000 4 105 360 4800 5100 0..*\001
+4 1 0 50 0 12 11 0.0000 4 105 360 5700 7800 0..*\001
+4 1 0 50 0 12 12 0.0000 4 180 1155 4650 8550 all_headers\001
+4 1 0 50 0 12 11 0.0000 4 105 360 4650 9000 0..*\001
+4 1 0 50 0 12 12 0.0000 4 135 525 9300 3300 files\001
+4 1 0 50 0 12 12 0.0000 4 180 525 12300 3300 paths\001
+4 1 0 50 0 12 11 0.0000 4 105 360 9300 3600 0..*\001
+4 1 0 50 0 12 11 0.0000 4 105 360 12300 3600 0..*\001
+4 1 0 50 0 12 11 0.0000 4 105 360 8550 5100 0..*\001
+4 1 0 50 0 12 12 0.0000 4 180 735 12450 4500 RB_Path\001
+4 1 0 50 0 12 12 0.0000 4 180 420 11400 4350 path\001
+4 1 0 50 0 12 12 0.0000 4 90 420 12450 4950 name\001
+4 1 0 50 0 12 12 0.0000 4 90 420 9750 4950 name\001
+4 1 0 50 0 12 12 0.0000 4 180 735 9750 4500 RB_File\001
+4 1 0 50 0 12 12 0.0000 4 135 735 9750 5250 docname\001
+4 1 0 50 0 12 12 0.0000 4 135 735 12450 5250 docname\001
+4 1 0 50 0 12 12 0.0000 4 165 630 7650 8250 parent\001
+4 1 0 50 0 12 12 0.0000 4 135 525 3300 11100 links\001
+4 1 0 50 0 12 11 0.0000 4 105 360 3300 11550 0..*\001
--- /dev/null
+
+<section>
+<title>Suggestions and Bugs</title>
+ <para>If you find any bugs, catch them, put them in a jar, and
+ send them to: Frans Slothouber at rfsber {at} xs4all.nl. Suggestions are also welcome at
+ this address. Flames can be directed to the sun.</para>
+</section>
+
--- /dev/null
+<!-- $Id: configuration.xml,v 1.9 2007/05/11 08:16:44 thuffir Exp $ -->
+
+<section id="customizing">
+<title id="customizing.title">Customizing ROBODoc</title>
+
+<para> ROBODoc can be configured with a configuration file called
+ <filename>robodoc.rc</filename>. With it you can define item
+ names, frequently used options, and translations for English
+ terms. One should note that if a configuration file is specified,
+ its definitions over-ride ROBODoc internal (i.e. built-in) settings.
+ This is a feature; some arbitrary language may include syntax
+ which conflicts with ROBODoc's internal markers. By taking advantage
+ of a configuration file, these sort of issues and conflicts
+ can be circumvented. An example is shown below.
+</para>
+
+<programlisting>
+# Example robodoc.rc
+#
+items:
+ NAME
+ FUNCTION
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+ SIDE EFFECTS
+ HISTORY
+ BUGS
+ EXAMPLE
+ignore items:
+ HISTORY
+ BUGS
+item order:
+ FUNCTION
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+source items:
+ SYNOPSIS
+preformatted items:
+ INPUTS
+ OUTPUTS
+format items:
+ FUNCTION
+ SIDE EFFECTS
+options:
+ --src ./source
+ --doc ./doc
+ --html
+ --multidoc
+ --index
+ --tabsize 8
+headertypes:
+ J "Projects" robo_projects 2
+ F "Files" robo_files 1
+ e "Makefile Entries" robo_mk_entries
+ x "System Tests" robo_syst_tests
+ q Queries robo_queries
+ignore files:
+ README
+ CVS
+ *.bak
+ *~
+ "a test_*"
+accept files:
+ *.c
+ *.h
+ *.pl
+header markers:
+ /****
+ #****
+remark markers:
+ *
+ #
+end markers:
+ ****
+ #****
+header separate characters:
+ ,
+header ignore characters:
+ [
+remark begin markers:
+ /*
+remark end markers:
+ */
+source line comments:
+ //
+keywords:
+ if
+ do
+ while
+ for
+</programlisting>
+
+<para>The configuration file consists of a number of blocks.
+ Each block starts with a name followed by a
+ <literal>:</literal>.
+ In each block you define a number of values. Each value must
+ start with at least one space.
+</para>
+
+<section><title>items block</title>
+ <para>In this block you can define the names of items that
+ ROBODoc should recognize. Item names can consist of more than
+ one word but should be written in all uppercase characters.
+ Define one item name per line. You do not need to put quotes
+ around them if they contain spaces.
+ </para>
+
+ <para>If you do not define an items block ROBODoc uses its
+ default list of item names. If you define an items block
+ ROBODoc uses only those item names, any of the default items names
+ (except SOURCE) are no longer recognized.</para>
+</section>
+
+<section><title>ignore items block</title>
+ <para>In this block you can define the names of items that
+ ROBODoc should ignore when generating documentation.
+ This can be useful if you want to create documentation
+ for a client, but for instance do not want to include
+ the history items and bugs items.</para>
+</section>
+
+<section><title>item order block</title>
+ <para>In this block you can define the order in which items
+ are to appear in the generated documentation.
+ The items listed here will appear in the given order in the
+ generated documentation. Any remaining items appear
+ after these items in the order they were found in the header.
+ This allows you to make the documentation look more uniform.
+ </para>
+</section>
+
+<section id="sourceitems">
+<title id="sourceitems.title">source items block</title>
+ <para>In this block you can define the names of items that
+ ROBODoc handles in the same way as the SOURCE item.
+ </para>
+</section>
+
+<section><title>preformatted items block</title>
+ <para>
+ In this block you can define the names of items that should be always
+ preformatted. This is useful if you have the
+ <option><xref linkend="nopre" endterm="nopre.term" /></option> option
+ specified and want specific items (like input and output lists) to be
+ automatically preformatted. See
+ <xref linkend="formatting" endterm="formatting.title" /> for more
+ information.
+ </para>
+</section>
+
+<section><title>format items block</title>
+ <para>
+ In this block you can define the names of items that should be formatted by
+ the <xref linkend="formatting" endterm="formatting.title" /> feature
+ (like function descriptions) even if the
+ <option><xref linkend="nopre" endterm="nopre.term" /></option>
+ option has not been specified.
+ </para>
+</section>
+
+<section><title>options block</title>
+ <para>In this block you can define frequently used options.
+ The options you specify here are added to any options you
+ specify on the command line.</para>
+ <para>
+ See <xref linkend="options" endterm="options.title" />.
+ </para>
+</section>
+
+<section id="headertypes_block">
+<title id="headertypes_block.title">headertypes block</title>
+ <para>In this block you can define your own header types.
+ These are added to the existing header types. Each new
+ header type requires three parameters: the character used to
+ indicate a header of this type, the title for this type as
+ used in the master index and the name of the file in which the
+ index of this type is stored. If you use a character of an
+ existing header type, this header type is overwritten.
+ </para>
+ <para>
+ Additionally the sorting priority can also be specified for each header
+ type. Headers with higher priority will appear earlier in the
+ generated output. (For example you can make module definitions appear
+ at the beginning of a page.) If this parameter is omitted, the header
+ will have the priority 0 (lowest) by default. All pre-defined header
+ types have zero priority, except
+ "<xref linkend="header_type_h" endterm="header_type_h" />",
+ which has the sorting priority 1.
+ See <xref linkend="header_types" endterm="header_types.title" />.
+ </para>
+</section>
+
+<section><title>ignore files block</title>
+ <para>In this block you can define the names of files or
+ directories that ROBODoc should ignore while scanning
+ the directory tree for source files. You can use the
+ wildcard symbols <literal>*</literal> and
+ <literal>?</literal>. If you use spaces in the expression
+ enclose the expression in quotes.
+ </para>
+
+ <para> For instance, the rc file above causes ROBODoc
+ to skip all <filename>README</filename> files, all files with
+ the name <filename>CVS</filename> (these are usually
+ directories). It also skips all files with a name that ends
+ with <filename>.bak</filename> or <filename>~</filename> or
+ that start with <filename>a test_</filename> </para>
+
+ <para>Normally you specify the names of directories here
+ and do the filtering of file names with a accept files block.
+ </para>
+</section>
+
+<section><title>accept files block</title>
+ <para>In this block you can define the names of files that
+ robodoc should accept. This test is carried out after the
+ 'ignore files' filtering is performed. Any files that do
+ not match the patterns in this block are ignored by ROBODoc.
+ </para>
+
+ <para>
+ If there is no accept files block all files are accepted.
+ </para>
+</section>
+
+<section><title>header markers block</title>
+ <para>
+ In this block you define what ROBODoc will recognize
+ as start of a header. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </para>
+</section>
+
+<section><title>remark markers block</title>
+ <para>
+ In this block you define what ROBODoc will recognize
+ as start of remark. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </para>
+</section>
+
+<section><title>end markers block</title>
+ <para>
+ In this block you define what ROBODoc will recognize
+ as end of a header. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </para>
+</section>
+
+<section id="separate_characters_block">
+<title id="separate_characters_block.title">header separate characters block</title>
+ <para>
+ In this block you can specify the separation character(s) for multiple
+ header names. The default character is "<literal>,</literal>" (comma) if
+ this block is missing. See
+ <xref linkend="preparing" endterm="preparing.title" />
+ for more information.
+ </para>
+</section>
+
+<section id="header_ignore_characters_block">
+<title id="header_ignore_characters_block.title">header ignore characters block</title>
+ <para>
+ In this block you can specify character(s) where the evaluation of a header
+ should stop. The default character is "<literal>[</literal>" if this block
+ is missing. This is useful to supply additional information to the header
+ that ROBODoc should not care about.
+ </para>
+ <para>
+ For example one can include the version number of the function:
+<programlisting>
+ ****f* financial.library/StealMoney [2.0]
+</programlisting>
+ </para>
+</section>
+
+<section id="remark_begin_end"><title id="remark_begin_end.title">
+remark begin markers and remark end markers block</title>
+ <para>
+ Some languages allow remarks to span more than one line. They
+ start a remark with a begin marker and end it with another
+ marker. For instance in C you can have:
+ </para>
+<programlisting>
+ /* This is a
+ multi-line remark.
+ */
+</programlisting>
+ <para>
+ Here the markers are <literal>/*</literal> and <literal>*/</literal>.
+ If you tell ROBODoc what these markers are in a remark begin
+ markers block and remark end markers block. ROBODoc can skip
+ them in source items.</para>
+
+ <para>We illustrate this with an example. Say we have a language
+ that start a remark with <literal>|*</literal> and
+ <literal>*|</literal>. We define <literal>SYNOPSIS</literal>
+ to be a source like item. If we now run ROBODoc on the
+ without a remark begin markers and
+ end markers block on the following piece of source, </para>
+
+<programlisting>
+|****f* Bar/foo
+ * FUNCTION
+ * foo computes the foo factor.
+ * SYNOPSIS
+ *|
+int foo( float correction )
+|*
+ * BUGS
+ * None
+ * SOURCE
+ *| .
+{
+ return correction * 42.0;
+}
+|*****|
+</programlisting>
+
+<para>the extracted documentation will look like:</para>
+
+<literallayout class="monospaced">
+ FUNCTION
+ foo computes the foo factor.
+ SYNOPSIS
+ *|
+ int foo( float correction )
+ |*
+ BUGS
+ None
+ SOURCE
+ *| .
+ {
+ return correction * 42.0;
+ }
+</literallayout>
+
+<para>because ROBODoc considers
+ <literal>|*</literal> and <literal>*|</literal>.to be part of the
+ source, and not part of the begin or end of a header.</para>
+
+<para> If you add</para>
+<programlisting>
+remark begin markers:
+ |*
+remark end markers:
+ *|
+</programlisting>
+<para>the output will look like:</para>
+<literallayout class="monospaced">
+ FUNCTION
+ foo computes the foo factor.
+ SYNOPSIS
+ int foo( float correction )
+ BUGS
+ None
+ SOURCE
+ {
+ return correction * 42.0;
+ }
+</literallayout>
+
+<para>
+ These markers will also be used to highlight block comments if the
+ <option>
+ <xref linkend="syntaxcolors_enable" endterm="syntaxcolors_enable.term" />
+ <xref linkend="syntaxcolors_enable_block_comments" endterm="syntaxcolors_enable_block_comments.option" />
+ </option>
+ option (or the
+ <option>
+ <xref linkend="syntaxcolors" endterm="syntaxcolors.term" />
+ </option>
+ option) is specified (Similar to
+ <xref linkend="linecomments" endterm="linecomments.title" />).
+</para>
+
+</section>
+
+<section id="linecomments"><title id="linecomments.title">source line comments block</title>
+ <para>
+ Here you can specify markers which start whole line comments.
+ (Comments that span until the end of line like "<literal>//</literal>",
+ "<literal>REM</literal>", "<literal>;</literal>", etc...)
+ These lines will be highlighted in the generated <literal>HTML</literal>
+ output if the
+ <option>
+ <xref linkend="syntaxcolors_enable" endterm="syntaxcolors_enable.term" />
+ <xref linkend="syntaxcolors_enable_line_comments" endterm="syntaxcolors_enable_line_comments.option" />
+ </option>
+ option (or the
+ <option>
+ <xref linkend="syntaxcolors" endterm="syntaxcolors.term" />
+ </option>
+ option) has been specified.</para>
+ <para>
+ You don't need this if you have the
+ <option>
+ <xref linkend="cmode" endterm="cmode.term" />
+ </option>
+ option specified.
+ </para>
+ <para>
+ The default highlighting color can be changed in the <literal>CSS</literal>
+ file (<literal>span.comment</literal>).
+ </para>
+</section>
+
+<section id="keywords"><title id="keywords.title">keywords block</title>
+ <para>
+ Here you can specify the keywords in your <literal>SOURCE</literal> items.
+ These keywords will be highlighted in the generated <literal>HTML</literal>
+ output if the
+ <option>
+ <xref linkend="syntaxcolors_enable" endterm="syntaxcolors_enable.term" />
+ <xref linkend="syntaxcolors_enable_keywords" endterm="syntaxcolors_enable_keywords.option" />
+ </option>
+ option (or the
+ <option>
+ <xref linkend="syntaxcolors" endterm="syntaxcolors.term" />
+ </option>
+ option) has been specified. Keywords meant to be the language native ones
+ (<literal>for</literal>, <literal>if</literal>, etc...).
+ </para>
+ <para>
+ You don't need this if you have the
+ <option>
+ <xref linkend="cmode" endterm="cmode.term" />
+ </option>
+ option specified.
+ </para>
+ <para>
+ The default highlighting color can be changed in the <literal>CSS</literal>
+ file (<literal>span.keyword</literal>).
+ </para>
+</section>
+
+ <section><title>Configuration file location</title>
+
+ <para>ROBODoc searches the your current directory for the
+ <filename>robodoc.rc</filename> file. If it can't find
+ it there it will search in <literal>$HOME/</literal>, then
+ <filename>$HOMEDRIVE$HOMEPATH/</filename>, and finally in
+ <filename>/usr/share/robodoc/</filename>.</para>
+
+ <para>With the <option>--rc</option> option you can tell ROBODoc to
+ use a different file than <filename>robodoc.rc</filename> as
+ configuration file. This is handy if you want to create
+ documentation in different formats. For instance: </para>
+
+<programlisting>
+robodoc --rc htmlsingle.rc
+robodoc --rc rtfsingle.rc
+robodoc --rc htmlmulti.rc
+</programlisting>
+
+ </section>
+
+</section>
+
--- /dev/null
+<!-- vi: ff=unix -->
+<!-- *********************************************************************
+ *** THIS IS THE FLATTENED DTD. DO NOT EDIT THIS DTD BY HAND, EDIT ***
+ *** THE CUSTOMIZATION LAYER AND REGENERATE THE FLATTENED DTD! ********
+ ********************************************************************* -->
+
+<!-- ...................................................................... -->
+<!-- Simplified DocBook DTD V1.1 .......................................... -->
+<!-- File sdocbook-custom.dtd ............................................. -->
+
+<!-- Copyright 1992-2002 HaL Computer Systems, Inc.,
+ O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software
+ Corporation, Norman Walsh, Sun Microsystems, Inc., and the
+ Organization for the Advancement of Structured Information
+ Standards (OASIS).
+
+ $Id: sdocbook.dtd,v 1.2 2007/01/13 11:27:32 gumpu Exp $
+
+ Permission to use, copy, modify and distribute the DocBook XML DTD
+ and its accompanying documentation for any purpose and without fee
+ is hereby granted in perpetuity, provided that the above copyright
+ notice and this paragraph appear in all copies. The copyright
+ holders make no representation about the suitability of the DTD for
+ any purpose. It is provided "as is" without expressed or implied
+ warranty.
+
+ If you modify the Simplified DocBook DTD in any way, except for
+ declaring and referencing additional sets of general entities and
+ declaring additional notations, label your DTD as a variant of
+ DocBook. See the maintenance documentation for more information.
+
+ Please direct all questions, bug reports, or suggestions for
+ changes to the docbook@lists.oasis-open.org mailing list. For more
+ information, see http://www.oasis-open.org/docbook/.
+-->
+
+<!-- ...................................................................... -->
+
+<!-- This is the driver file for V1.1 of the Simplified DocBook XML DTD.
+ Please use the following formal public identifier to identify it:
+
+ "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+
+ For example:
+
+ <!DOCTYPE article
+ PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+ "http://www.oasis-open.org/docbook/xml/simple/1.1/sdocbook.dtd"
+ [...]>
+-->
+
+<!-- ...................................................................... -->
+
+
+<!ENTITY % local.list.class "">
+<!ENTITY % list.class
+ "itemizedlist|orderedlist
+ |variablelist %local.list.class;">
+<!ENTITY % local.admon.class "">
+<!ENTITY % admon.class
+ "note %local.admon.class;">
+<!ENTITY % local.linespecific.class "">
+<!ENTITY % linespecific.class
+ "literallayout|programlisting %local.linespecific.class;">
+<!ENTITY % local.para.class "">
+<!ENTITY % para.class
+ "para %local.para.class;">
+<!ENTITY % local.informal.class "">
+<!ENTITY % informal.class
+ "blockquote
+ |mediaobject
+ |informaltable %local.informal.class;">
+<!ENTITY % local.formal.class "">
+<!ENTITY % formal.class
+ "example|figure|table %local.formal.class;">
+<!ENTITY % local.compound.class "">
+<!ENTITY % compound.class
+ "sidebar %local.compound.class;">
+<!ENTITY % local.genobj.class "">
+<!ENTITY % genobj.class "anchor %local.genobj.class;">
+<!ENTITY % local.descobj.class "">
+<!ENTITY % descobj.class
+ "abstract|authorblurb|epigraph
+ %local.descobj.class;">
+<!ENTITY % local.xref.char.class "">
+<!ENTITY % xref.char.class "footnoteref|xref %local.xref.char.class;">
+<!ENTITY % local.gen.char.class "">
+<!ENTITY % gen.char.class
+ "abbrev|acronym|citetitle|emphasis|footnote|phrase
+ |quote|trademark %local.gen.char.class;">
+<!ENTITY % local.link.char.class "">
+<!ENTITY % link.char.class
+ "link|ulink %local.link.char.class;">
+<!ENTITY % local.tech.char.class "">
+<!ENTITY % tech.char.class
+ "command|computeroutput
+ |email|filename
+ |literal
+ |option
+ |replaceable
+ |systemitem|userinput
+ %local.tech.char.class;">
+<!ENTITY % local.base.char.class "">
+<!ENTITY % base.char.class "anchor %local.base.char.class;">
+<!ENTITY % local.docinfo.char.class "">
+<!ENTITY % docinfo.char.class
+ "author|corpauthor|othercredit
+ |revhistory
+ %local.docinfo.char.class;">
+<!ENTITY % local.other.char.class "">
+<!ENTITY % other.char.class
+ "subscript|superscript %local.other.char.class;">
+<!ENTITY % local.inlineobj.char.class "">
+<!ENTITY % inlineobj.char.class
+ "inlinemediaobject %local.inlineobj.char.class;">
+<!ENTITY % local.effectivity.attrib "">
+<!ENTITY % effectivity.attrib
+ "%local.effectivity.attrib;"
+>
+<!ENTITY % local.common.attrib "">
+<!ENTITY % common.attrib
+ "id ID #IMPLIED
+ lang CDATA #IMPLIED
+ remap CDATA #IMPLIED
+ revisionflag (changed
+ |added
+ |deleted
+ |off) #IMPLIED
+ %local.common.attrib;"
+>
+<!ENTITY % local.person.ident.mix "">
+<!ENTITY % person.ident.mix
+ "honorific|firstname|surname|lineage|othername|affiliation
+ |authorblurb
+ %local.person.ident.mix;">
+<!ENTITY % local.bibliocomponent.mix "">
+<!ENTITY % bibliocomponent.mix
+ "abbrev|abstract|author
+ |authorgroup|bibliomisc
+ |copyright|corpauthor|date|edition
+ |editor|issuenum
+ |othercredit
+ |pubdate|publishername
+ |releaseinfo|revhistory
+ |subtitle|title|titleabbrev|volumenum|citetitle
+ |%person.ident.mix;
+ %local.bibliocomponent.mix;">
+<!ENTITY % local.component.mix "">
+<!ENTITY % component.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%formal.class; |%compound.class;
+ |%genobj.class; |%descobj.class;
+ %local.component.mix;">
+<!ENTITY % local.sidebar.mix "">
+<!ENTITY % sidebar.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%formal.class;
+ |%genobj.class;
+ %local.sidebar.mix;">
+<!ENTITY % local.revdescription.mix "">
+<!ENTITY % revdescription.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%formal.class;
+ |%genobj.class;
+ %local.revdescription.mix;">
+<!ENTITY % local.footnote.mix "">
+<!ENTITY % footnote.mix
+ "%list.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ %local.footnote.mix;">
+<!ENTITY % local.example.mix "">
+<!ENTITY % example.mix
+ "%list.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ %local.example.mix;">
+<!ENTITY % local.admon.mix "">
+<!ENTITY % admon.mix
+ "%list.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%formal.class;
+ %local.admon.mix;">
+<!ENTITY % local.figure.mix "">
+<!ENTITY % figure.mix
+ "%linespecific.class;
+ |%informal.class;
+ %local.figure.mix;">
+<!ENTITY % local.tabentry.mix "">
+<!ENTITY % tabentry.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |mediaobject
+ %local.tabentry.mix;">
+<!ENTITY % local.legalnotice.mix "">
+<!ENTITY % legalnotice.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |blockquote
+ %local.legalnotice.mix;">
+<!ENTITY % local.textobject.mix "">
+<!ENTITY % textobject.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |blockquote
+ %local.textobject.mix;">
+<!ENTITY % local.listpreamble.mix "">
+<!ENTITY % listpreamble.mix
+ " %admon.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%genobj.class; |%descobj.class;
+ %local.listpreamble.mix;">
+<!ENTITY % local.para.char.mix "">
+<!ENTITY % para.char.mix
+ "#PCDATA
+ |%xref.char.class; |%gen.char.class;
+ |%link.char.class; |%tech.char.class;
+ |%base.char.class;
+ |%other.char.class; |%inlineobj.char.class;
+ %local.para.char.mix;">
+<!ENTITY % local.title.char.mix "">
+<!ENTITY % title.char.mix
+ "#PCDATA
+ |%xref.char.class; |%gen.char.class;
+ |%link.char.class; |%tech.char.class;
+ |%base.char.class;
+ |%docinfo.char.class;
+ |%other.char.class; |%inlineobj.char.class;
+ %local.title.char.mix;">
+<!ENTITY % local.cptr.char.mix "">
+<!ENTITY % cptr.char.mix
+ "#PCDATA
+ |%link.char.class; |%tech.char.class;
+ |%base.char.class;
+ |%other.char.class; |inlinemediaobject
+ %local.cptr.char.mix;">
+<!ENTITY % local.word.char.mix "">
+<!ENTITY % word.char.mix
+ "#PCDATA
+ |acronym|emphasis|trademark
+ |%link.char.class;
+ |%base.char.class;
+ |%other.char.class; |inlinemediaobject
+ %local.word.char.mix;">
+<!ENTITY % local.docinfo.char.mix "">
+<!ENTITY % docinfo.char.mix
+ "#PCDATA
+ |%link.char.class;
+ |emphasis|trademark
+ |replaceable
+ |%other.char.class; |inlinemediaobject
+ %local.docinfo.char.mix;">
+<!ENTITY % tbl.table.mdl
+ "((title, (mediaobject+|tgroup+))
+ |(caption, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+)))">
+<!ENTITY % local.divcomponent.mix "">
+<!ENTITY % divcomponent.mix
+ "%list.class; |%admon.class;
+ |%linespecific.class;
+ |%para.class; |%informal.class;
+ |%formal.class; |%compound.class;
+ |%descobj.class;
+ %local.divcomponent.mix;">
+<!ENTITY % bookcomponent.content
+ "((%divcomponent.mix;)+, section*)
+ | section+">
+<!ENTITY Aacute "Á" >
+<!ENTITY aacute "á" >
+<!ENTITY Acirc "Â" >
+<!ENTITY acirc "â" >
+<!ENTITY AElig "Æ" >
+<!ENTITY aelig "æ" >
+<!ENTITY Agrave "À" >
+<!ENTITY agrave "à" >
+<!ENTITY Aring "Å" >
+<!ENTITY aring "å" >
+<!ENTITY Atilde "Ã" >
+<!ENTITY atilde "ã" >
+<!ENTITY Auml "Ä" >
+<!ENTITY auml "ä" >
+<!ENTITY Ccedil "Ç" >
+<!ENTITY ccedil "ç" >
+<!ENTITY Eacute "É" >
+<!ENTITY eacute "é" >
+<!ENTITY Ecirc "Ê" >
+<!ENTITY ecirc "ê" >
+<!ENTITY Egrave "È" >
+<!ENTITY egrave "è" >
+<!ENTITY ETH "Ð" >
+<!ENTITY eth "ð" >
+<!ENTITY Euml "Ë" >
+<!ENTITY euml "ë" >
+<!ENTITY Iacute "Í" >
+<!ENTITY iacute "í" >
+<!ENTITY Icirc "Î" >
+<!ENTITY icirc "î" >
+<!ENTITY Igrave "Ì" >
+<!ENTITY igrave "ì" >
+<!ENTITY Iuml "Ï" >
+<!ENTITY iuml "ï" >
+<!ENTITY Ntilde "Ñ" >
+<!ENTITY ntilde "ñ" >
+<!ENTITY Oacute "Ó" >
+<!ENTITY oacute "ó" >
+<!ENTITY Ocirc "Ô" >
+<!ENTITY ocirc "ô" >
+<!ENTITY Ograve "Ò" >
+<!ENTITY ograve "ò" >
+<!ENTITY Oslash "Ø" >
+<!ENTITY oslash "ø" >
+<!ENTITY Otilde "Õ" >
+<!ENTITY otilde "õ" >
+<!ENTITY Ouml "Ö" >
+<!ENTITY ouml "ö" >
+<!ENTITY szlig "ß" >
+<!ENTITY THORN "Þ" >
+<!ENTITY thorn "þ" >
+<!ENTITY Uacute "Ú" >
+<!ENTITY uacute "ú" >
+<!ENTITY Ucirc "Û" >
+<!ENTITY ucirc "û" >
+<!ENTITY Ugrave "Ù" >
+<!ENTITY ugrave "ù" >
+<!ENTITY Uuml "Ü" >
+<!ENTITY uuml "ü" >
+<!ENTITY Yacute "Ý" >
+<!ENTITY yacute "ý" >
+<!ENTITY yuml "ÿ" >
+<!ENTITY amp "&#38;" >
+<!ENTITY apos "'" >
+<!ENTITY ast "*" >
+<!ENTITY brvbar "¦" >
+<!ENTITY bsol "\" >
+<!ENTITY cent "¢" >
+<!ENTITY colon ":" >
+<!ENTITY comma "," >
+<!ENTITY commat "@" >
+<!ENTITY copy "©" >
+<!ENTITY curren "¤" >
+<!ENTITY darr "↓" >
+<!ENTITY deg "°" >
+<!ENTITY divide "÷" >
+<!ENTITY dollar "$" >
+<!ENTITY equals "=" >
+<!ENTITY excl "!" >
+<!ENTITY frac12 "½" >
+<!ENTITY frac14 "¼" >
+<!ENTITY frac18 "⅛" >
+<!ENTITY frac34 "¾" >
+<!ENTITY frac38 "⅜" >
+<!ENTITY frac58 "⅝" >
+<!ENTITY frac78 "⅞" >
+<!ENTITY gt ">" >
+<!ENTITY half "½" >
+<!ENTITY horbar "―" >
+<!ENTITY hyphen "‐" >
+<!ENTITY iexcl "¡" >
+<!ENTITY iquest "¿" >
+<!ENTITY laquo "«" >
+<!ENTITY larr "←" >
+<!ENTITY lcub "{" >
+<!ENTITY ldquo "“" >
+<!ENTITY lowbar "_" >
+<!ENTITY lpar "(" >
+<!ENTITY lsqb "[" >
+<!ENTITY lsquo "‘" >
+<!ENTITY lt "&#60;" >
+<!ENTITY micro "µ" >
+<!ENTITY middot "·" >
+<!ENTITY nbsp " " >
+<!ENTITY not "¬" >
+<!ENTITY num "#" >
+<!ENTITY ohm "Ω" >
+<!ENTITY ordf "ª" >
+<!ENTITY ordm "º" >
+<!ENTITY para "¶" >
+<!ENTITY percnt "%" >
+<!ENTITY period "." >
+<!ENTITY plus "+" >
+<!ENTITY plusmn "±" >
+<!ENTITY pound "£" >
+<!ENTITY quest "?" >
+<!ENTITY quot """ >
+<!ENTITY raquo "»" >
+<!ENTITY rarr "→" >
+<!ENTITY rcub "}" >
+<!ENTITY rdquo "”" >
+<!ENTITY reg "®" >
+<!ENTITY rpar ")" >
+<!ENTITY rsqb "]" >
+<!ENTITY rsquo "’" >
+<!ENTITY sect "§" >
+<!ENTITY semi ";" >
+<!ENTITY shy "­" >
+<!ENTITY sol "/" >
+<!ENTITY sung "♪" >
+<!ENTITY sup1 "¹" >
+<!ENTITY sup2 "²" >
+<!ENTITY sup3 "³" >
+<!ENTITY times "×" >
+<!ENTITY trade "™" >
+<!ENTITY uarr "↑" >
+<!ENTITY verbar "|" >
+<!ENTITY yen "¥" >
+<!ENTITY blank "␣" >
+<!ENTITY blk12 "▒" >
+<!ENTITY blk14 "░" >
+<!ENTITY blk34 "▓" >
+<!ENTITY block "█" >
+<!ENTITY bull "•" >
+<!ENTITY caret "⁁" >
+<!ENTITY check "✓" >
+<!ENTITY cir "○" >
+<!ENTITY clubs "♣" >
+<!ENTITY copysr "℗" >
+<!ENTITY cross "✗" >
+<!ENTITY Dagger "‡" >
+<!ENTITY dagger "†" >
+<!ENTITY dash "‐" >
+<!ENTITY diams "♦" >
+<!ENTITY dlcrop "⌍" >
+<!ENTITY drcrop "⌌" >
+<!ENTITY dtri "▿" >
+<!ENTITY dtrif "▾" >
+<!ENTITY emsp " " >
+<!ENTITY emsp13 " " >
+<!ENTITY emsp14 " " >
+<!ENTITY ensp " " >
+<!ENTITY female "♀" >
+<!ENTITY ffilig "ffi" >
+<!ENTITY fflig "ff" >
+<!ENTITY ffllig "ffl" >
+<!ENTITY filig "fi" >
+<!ENTITY flat "♭" >
+<!ENTITY fllig "fl" >
+<!ENTITY frac13 "⅓" >
+<!ENTITY frac15 "⅕" >
+<!ENTITY frac16 "⅙" >
+<!ENTITY frac23 "⅔" >
+<!ENTITY frac25 "⅖" >
+<!ENTITY frac35 "⅗" >
+<!ENTITY frac45 "⅘" >
+<!ENTITY frac56 "⅚" >
+<!ENTITY hairsp " " >
+<!ENTITY hearts "♥" >
+<!ENTITY hellip "…" >
+<!ENTITY hybull "⁃" >
+<!ENTITY incare "℅" >
+<!ENTITY ldquor "„" >
+<!ENTITY lhblk "▄" >
+<!ENTITY loz "◊" >
+<!ENTITY lozf "⧫" >
+<!ENTITY lsquor "‚" >
+<!ENTITY ltri "◃" >
+<!ENTITY ltrif "◂" >
+<!ENTITY male "♂" >
+<!ENTITY malt "✠" >
+<!ENTITY marker "▮" >
+<!ENTITY mdash "—" >
+<!ENTITY mldr "…" >
+<!ENTITY natur "♮" >
+<!ENTITY ndash "–" >
+<!ENTITY nldr "‥" >
+<!ENTITY numsp " " >
+<!ENTITY phone "☎" >
+<!ENTITY puncsp " " >
+<!ENTITY rdquor "”" >
+<!ENTITY rect "▭" >
+<!ENTITY rsquor "’" >
+<!ENTITY rtri "▹" >
+<!ENTITY rtrif "▸" >
+<!ENTITY rx "℞" >
+<!ENTITY sext "✶" >
+<!ENTITY sharp "♯" >
+<!ENTITY spades "♠" >
+<!ENTITY squ "□" >
+<!ENTITY squf "▪" >
+<!ENTITY star "☆" >
+<!ENTITY starf "★" >
+<!ENTITY target "⌖" >
+<!ENTITY telrec "⌕" >
+<!ENTITY thinsp " " >
+<!ENTITY uhblk "▀" >
+<!ENTITY ulcrop "⌏" >
+<!ENTITY urcrop "⌎" >
+<!ENTITY utri "▵" >
+<!ENTITY utrif "▴" >
+<!ENTITY vellip "⋮" >
+<!ENTITY % local.notation.class "">
+<!ENTITY % notation.class
+ "BMP| CGM-CHAR | CGM-BINARY | CGM-CLEAR | DITROFF | DVI
+ | EPS | EQN | FAX | GIF | GIF87a | GIF89a
+ | JPG | JPEG | IGES | PCX
+ | PIC | PNG | PS | SGML | TBL | TEX | TIFF | WMF | WPG
+ | SVG | PDF | SWF
+ | linespecific
+ %local.notation.class;">
+<!NOTATION BMP PUBLIC
+"+//ISBN 0-7923-94.2-1::Graphic Notation//NOTATION Microsoft Windows bitmap//EN">
+<!NOTATION CGM-CHAR PUBLIC "ISO 8632/2//NOTATION Character encoding//EN">
+<!NOTATION CGM-BINARY PUBLIC "ISO 8632/3//NOTATION Binary encoding//EN">
+<!NOTATION CGM-CLEAR PUBLIC "ISO 8632/4//NOTATION Clear text encoding//EN">
+<!NOTATION DITROFF SYSTEM "DITROFF">
+<!NOTATION DVI SYSTEM "DVI">
+<!NOTATION EPS PUBLIC
+"+//ISBN 0-201-18127-4::Adobe//NOTATION PostScript Language Ref. Manual//EN">
+<!NOTATION EQN SYSTEM "EQN">
+<!NOTATION FAX PUBLIC
+"-//USA-DOD//NOTATION CCITT Group 4 Facsimile Type 1 Untiled Raster//EN">
+<!NOTATION GIF SYSTEM "GIF">
+<!NOTATION GIF87a PUBLIC
+"-//CompuServe//NOTATION Graphics Interchange Format 87a//EN">
+<!NOTATION GIF89a PUBLIC
+"-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">
+<!NOTATION JPG SYSTEM "JPG">
+<!NOTATION JPEG SYSTEM "JPG">
+<!NOTATION IGES PUBLIC
+"-//USA-DOD//NOTATION (ASME/ANSI Y14.26M-1987) Initial Graphics Exchange Specification//EN">
+<!NOTATION PCX PUBLIC
+"+//ISBN 0-7923-94.2-1::Graphic Notation//NOTATION ZSoft PCX bitmap//EN">
+<!NOTATION PIC SYSTEM "PIC">
+<!NOTATION PNG SYSTEM "http://www.w3.org/TR/REC-png">
+<!NOTATION PS SYSTEM "PS">
+<!NOTATION SGML PUBLIC
+"ISO 8879:1986//NOTATION Standard Generalized Markup Language//EN">
+<!NOTATION TBL SYSTEM "TBL">
+<!NOTATION TEX PUBLIC
+"+//ISBN 0-201-13448-9::Knuth//NOTATION The TeXbook//EN">
+<!NOTATION TIFF SYSTEM "TIFF">
+<!NOTATION WMF PUBLIC
+"+//ISBN 0-7923-94.2-1::Graphic Notation//NOTATION Microsoft Windows Metafile//EN">
+<!NOTATION WPG SYSTEM "WPG">
+<!NOTATION SVG SYSTEM "http://www.w3.org/TR/SVG/">
+<!NOTATION PDF SYSTEM "http://www.adobe.com/products/acrobat/adobepdf.html">
+<!NOTATION SWF SYSTEM "http://www.macromedia.com/software/flash">
+<!NOTATION linespecific SYSTEM "linespecific">
+<!ENTITY euro "€">
+<!ENTITY % yesorno.attvals "CDATA">
+<!ENTITY % formalobject.title.content "title, titleabbrev?">
+<!ENTITY % local.mediaobject.mix "">
+<!ENTITY % mediaobject.mix
+ "videoobject|audioobject|imageobject|textobject %local.mediaobject.mix;">
+<!ENTITY % idreq.attrib
+ "id ID #REQUIRED">
+<!ENTITY % lang.attrib
+ "lang CDATA #IMPLIED">
+<!ENTITY % remap.attrib
+ "remap CDATA #IMPLIED">
+<!ENTITY % role.attrib
+ "role CDATA #IMPLIED">
+<!ENTITY % xreflabel.attrib
+ "xreflabel CDATA #IMPLIED">
+<!ENTITY % revisionflag.attrib
+ "revisionflag (changed
+ |added
+ |deleted
+ |off) #IMPLIED">
+<!ENTITY % dir.attrib
+ "dir (ltr
+ |rtl
+ |lro
+ |rlo) #IMPLIED">
+<!ENTITY % xml-base.attrib
+ "xml:base CDATA #IMPLIED">
+<!ENTITY % label.attrib
+ "label CDATA #IMPLIED">
+<!ENTITY % linespecific.attrib
+ "format NOTATION
+ (linespecific) 'linespecific'
+ linenumbering (numbered|unnumbered) #IMPLIED
+ continuation (continues|restarts) #IMPLIED
+ startinglinenumber CDATA #IMPLIED
+ language CDATA #IMPLIED">
+<!ENTITY % linkendreq.attrib
+ "linkend IDREF #REQUIRED">
+<!ENTITY % local.mark.attrib "">
+<!ENTITY % mark.attrib
+ "mark CDATA #IMPLIED
+ %local.mark.attrib;"
+>
+<!ENTITY % moreinfo.attrib
+ "moreinfo (refentry|none) 'none'">
+<!ENTITY % pagenum.attrib
+ "pagenum CDATA #IMPLIED">
+<!ENTITY % local.status.attrib "">
+<!ENTITY % status.attrib
+ "status CDATA #IMPLIED
+ %local.status.attrib;"
+>
+<!ENTITY % width.attrib
+ "width CDATA #IMPLIED">
+<!ENTITY % local.title.attrib "">
+<!ENTITY % title.role.attrib "%role.attrib;">
+<!ELEMENT title (%title.char.mix;)*>
+<!ATTLIST title
+ %pagenum.attrib;
+ %common.attrib;
+ %title.role.attrib;
+ %local.title.attrib;
+>
+<!ENTITY % local.titleabbrev.attrib "">
+<!ENTITY % titleabbrev.role.attrib "%role.attrib;">
+<!ELEMENT titleabbrev (%title.char.mix;)*>
+<!ATTLIST titleabbrev
+ %common.attrib;
+ %titleabbrev.role.attrib;
+ %local.titleabbrev.attrib;
+>
+<!ENTITY % local.subtitle.attrib "">
+<!ENTITY % subtitle.role.attrib "%role.attrib;">
+<!ELEMENT subtitle (%title.char.mix;)*>
+<!ATTLIST subtitle
+ %common.attrib;
+ %subtitle.role.attrib;
+ %local.subtitle.attrib;
+>
+<!ENTITY % local.bibliomixed.attrib "">
+<!ENTITY % bibliomixed.role.attrib "%role.attrib;">
+<!ATTLIST bibliomixed
+ %common.attrib;
+ %bibliomixed.role.attrib;
+ %local.bibliomixed.attrib;
+>
+<!ENTITY % local.articleinfo.attrib "">
+<!ENTITY % articleinfo.role.attrib "%role.attrib;">
+<!ATTLIST articleinfo
+ %common.attrib;
+ %articleinfo.role.attrib;
+ %local.articleinfo.attrib;
+>
+<!ENTITY % bibliomset.role.attrib "%role.attrib;">
+<!ENTITY % local.bibliomset.attrib "">
+<!ELEMENT bibliomset (#PCDATA | %bibliocomponent.mix; | bibliomset)*
+ >
+<!ATTLIST bibliomset
+ relation CDATA #IMPLIED
+ %common.attrib;
+ %bibliomset.role.attrib;
+ %local.bibliomset.attrib;
+>
+<!ENTITY % local.bibliomisc.attrib "">
+<!ENTITY % bibliomisc.role.attrib "%role.attrib;">
+<!ELEMENT bibliomisc (%para.char.mix;)*>
+<!ATTLIST bibliomisc
+ %common.attrib;
+ %bibliomisc.role.attrib;
+ %local.bibliomisc.attrib;
+>
+<!ENTITY % local.subjectset.attrib "">
+<!ENTITY % subjectset.role.attrib "%role.attrib;">
+<!ELEMENT subjectset (subject+)>
+<!ATTLIST subjectset
+ scheme NMTOKEN #IMPLIED
+ %common.attrib;
+ %subjectset.role.attrib;
+ %local.subjectset.attrib;
+>
+<!ENTITY % local.subject.attrib "">
+<!ENTITY % subject.role.attrib "%role.attrib;">
+<!ELEMENT subject (subjectterm+)>
+<!ATTLIST subject
+ weight CDATA #IMPLIED
+ %common.attrib;
+ %subject.role.attrib;
+ %local.subject.attrib;
+>
+<!ENTITY % local.subjectterm.attrib "">
+<!ENTITY % subjectterm.role.attrib "%role.attrib;">
+<!ELEMENT subjectterm (#PCDATA)>
+<!ATTLIST subjectterm
+ %common.attrib;
+ %subjectterm.role.attrib;
+ %local.subjectterm.attrib;
+>
+<!ENTITY % local.keywordset.attrib "">
+<!ENTITY % keywordset.role.attrib "%role.attrib;">
+<!ELEMENT keywordset (keyword+)>
+<!ATTLIST keywordset
+ %common.attrib;
+ %keywordset.role.attrib;
+ %local.keywordset.attrib;
+>
+<!ENTITY % local.keyword.attrib "">
+<!ENTITY % keyword.role.attrib "%role.attrib;">
+<!ELEMENT keyword (#PCDATA)>
+<!ATTLIST keyword
+ %common.attrib;
+ %keyword.role.attrib;
+ %local.keyword.attrib;
+>
+<!ENTITY % local.sidebar.attrib "">
+<!ENTITY % sidebar.role.attrib "%role.attrib;">
+<!ATTLIST sidebar
+ %common.attrib;
+ %sidebar.role.attrib;
+ %local.sidebar.attrib;
+>
+<!ENTITY % local.abstract.attrib "">
+<!ENTITY % abstract.role.attrib "%role.attrib;">
+<!ELEMENT abstract (title?, (%para.class;)+)>
+<!ATTLIST abstract
+ %common.attrib;
+ %abstract.role.attrib;
+ %local.abstract.attrib;
+>
+<!ENTITY % local.authorblurb.attrib "">
+<!ENTITY % authorblurb.role.attrib "%role.attrib;">
+<!ELEMENT authorblurb (title?, (%para.class;)+)>
+<!ATTLIST authorblurb
+ %common.attrib;
+ %authorblurb.role.attrib;
+ %local.authorblurb.attrib;
+>
+<!ENTITY % local.blockquote.attrib "">
+<!ENTITY % blockquote.role.attrib "%role.attrib;">
+<!ATTLIST blockquote
+ %common.attrib;
+ %blockquote.role.attrib;
+ %local.blockquote.attrib;
+>
+<!ENTITY % local.attribution.attrib "">
+<!ENTITY % attribution.role.attrib "%role.attrib;">
+<!ELEMENT attribution (%para.char.mix;)*>
+<!ATTLIST attribution
+ %common.attrib;
+ %attribution.role.attrib;
+ %local.attribution.attrib;
+>
+<!ENTITY % local.epigraph.attrib "">
+<!ENTITY % epigraph.role.attrib "%role.attrib;">
+<!ELEMENT epigraph (attribution?, ((%para.class;)|literallayout)+)>
+<!ATTLIST epigraph
+ %common.attrib;
+ %epigraph.role.attrib;
+ %local.epigraph.attrib;
+>
+<!ENTITY % local.footnote.attrib "">
+<!ENTITY % footnote.role.attrib "%role.attrib;">
+<!ELEMENT footnote ((%footnote.mix;)+)
+ >
+<!ATTLIST footnote
+ %label.attrib;
+ %common.attrib;
+ %footnote.role.attrib;
+ %local.footnote.attrib;
+>
+<!ENTITY % local.para.attrib "">
+<!ENTITY % para.role.attrib "%role.attrib;">
+<!ATTLIST para
+ %common.attrib;
+ %para.role.attrib;
+ %local.para.attrib;
+>
+<!ENTITY % local.admon.attrib "">
+<!ENTITY % admon.role.attrib "%role.attrib;">
+<!ELEMENT note (title?, (%admon.mix;)+)
+ >
+<!ATTLIST note
+ %common.attrib;
+ %admon.role.attrib;
+ %local.admon.attrib;
+>
+<!ENTITY % local.itemizedlist.attrib "">
+<!ENTITY % itemizedlist.role.attrib "%role.attrib;">
+<!ATTLIST itemizedlist spacing (normal
+ |compact) #IMPLIED
+ %mark.attrib;
+ %common.attrib;
+ %itemizedlist.role.attrib;
+ %local.itemizedlist.attrib;
+>
+<!ENTITY % local.orderedlist.attrib "">
+<!ENTITY % orderedlist.role.attrib "%role.attrib;">
+<!ATTLIST orderedlist
+ numeration (arabic
+ |upperalpha
+ |loweralpha
+ |upperroman
+ |lowerroman) #IMPLIED
+ inheritnum (inherit
+ |ignore) "ignore"
+ continuation (continues
+ |restarts) "restarts"
+ spacing (normal
+ |compact) #IMPLIED
+ %common.attrib;
+ %orderedlist.role.attrib;
+ %local.orderedlist.attrib;
+>
+<!ENTITY % local.listitem.attrib "">
+<!ENTITY % listitem.role.attrib "%role.attrib;">
+<!ELEMENT listitem ((%component.mix;)+)>
+<!ATTLIST listitem
+ override CDATA #IMPLIED
+ %common.attrib;
+ %listitem.role.attrib;
+ %local.listitem.attrib;
+>
+<!ENTITY % local.variablelist.attrib "">
+<!ENTITY % variablelist.role.attrib "%role.attrib;">
+<!ATTLIST variablelist
+ termlength CDATA #IMPLIED
+ %common.attrib;
+ %variablelist.role.attrib;
+ %local.variablelist.attrib;
+>
+<!ENTITY % local.varlistentry.attrib "">
+<!ENTITY % varlistentry.role.attrib "%role.attrib;">
+<!ELEMENT varlistentry (term+, listitem)>
+<!ATTLIST varlistentry
+ %common.attrib;
+ %varlistentry.role.attrib;
+ %local.varlistentry.attrib;
+>
+<!ENTITY % local.term.attrib "">
+<!ENTITY % term.role.attrib "%role.attrib;">
+<!ELEMENT term (%para.char.mix;)*>
+<!ATTLIST term
+ %common.attrib;
+ %term.role.attrib;
+ %local.term.attrib;
+>
+<!ENTITY % local.example.attrib "">
+<!ENTITY % example.role.attrib "%role.attrib;">
+<!ATTLIST example
+ floatstyle CDATA #IMPLIED
+ %label.attrib;
+ %width.attrib;
+ %common.attrib;
+ %example.role.attrib;
+ %local.example.attrib;
+>
+<!ENTITY % local.programlisting.attrib "">
+<!ENTITY % programlisting.role.attrib "%role.attrib;">
+<!ATTLIST programlisting
+ %width.attrib;
+ %linespecific.attrib;
+ %common.attrib;
+ %programlisting.role.attrib;
+ %local.programlisting.attrib;
+>
+<!ENTITY % local.literallayout.attrib "">
+<!ENTITY % literallayout.role.attrib "%role.attrib;">
+<!ATTLIST literallayout
+ %width.attrib;
+ %linespecific.attrib;
+ class (monospaced|normal) "normal"
+ %common.attrib;
+ %literallayout.role.attrib;
+ %local.literallayout.attrib;
+>
+<!ENTITY % local.figure.attrib "">
+<!ENTITY % figure.role.attrib "%role.attrib;">
+<!ATTLIST figure
+ float %yesorno.attvals; '0'
+ floatstyle CDATA #IMPLIED
+ pgwide %yesorno.attvals; #IMPLIED
+ %label.attrib;
+ %common.attrib;
+ %figure.role.attrib;
+ %local.figure.attrib;
+>
+<!ENTITY % local.mediaobject.attrib "">
+<!ENTITY % mediaobject.role.attrib "%role.attrib;">
+<!ELEMENT mediaobject (objectinfo?,
+ (%mediaobject.mix;)+,
+ caption?)>
+<!ATTLIST mediaobject
+ %common.attrib;
+ %mediaobject.role.attrib;
+ %local.mediaobject.attrib;
+>
+<!ENTITY % local.inlinemediaobject.attrib "">
+<!ENTITY % inlinemediaobject.role.attrib "%role.attrib;">
+<!ELEMENT inlinemediaobject (objectinfo?,
+ (%mediaobject.mix;)+)>
+<!ATTLIST inlinemediaobject
+ %common.attrib;
+ %inlinemediaobject.role.attrib;
+ %local.inlinemediaobject.attrib;
+>
+<!ENTITY % local.videoobject.attrib "">
+<!ENTITY % videoobject.role.attrib "%role.attrib;">
+<!ELEMENT videoobject (objectinfo?, videodata)>
+<!ATTLIST videoobject
+ %common.attrib;
+ %videoobject.role.attrib;
+ %local.videoobject.attrib;
+>
+<!ENTITY % local.audioobject.attrib "">
+<!ENTITY % audioobject.role.attrib "%role.attrib;">
+<!ELEMENT audioobject (objectinfo?, audiodata)>
+<!ATTLIST audioobject
+ %common.attrib;
+ %audioobject.role.attrib;
+ %local.audioobject.attrib;
+>
+<!ENTITY % local.imageobject.attrib "">
+<!ENTITY % imageobject.role.attrib "%role.attrib;">
+<!ELEMENT imageobject (objectinfo?, imagedata)>
+<!ATTLIST imageobject
+ %common.attrib;
+ %imageobject.role.attrib;
+ %local.imageobject.attrib;
+>
+<!ENTITY % local.textobject.attrib "">
+<!ENTITY % textobject.role.attrib "%role.attrib;">
+<!ELEMENT textobject (objectinfo?, (phrase|textdata|(%textobject.mix;)+))>
+<!ATTLIST textobject
+ %common.attrib;
+ %textobject.role.attrib;
+ %local.textobject.attrib;
+>
+<!ENTITY % local.objectinfo.attrib "">
+<!ENTITY % objectinfo.role.attrib "%role.attrib;">
+<!ATTLIST objectinfo
+ %common.attrib;
+ %objectinfo.role.attrib;
+ %local.objectinfo.attrib;
+>
+<!ENTITY % local.objectdata.attrib "">
+<!ENTITY % objectdata.attrib
+ "
+ entityref ENTITY #IMPLIED
+ fileref CDATA #IMPLIED
+ format (%notation.class;)
+ #IMPLIED
+ srccredit CDATA #IMPLIED
+ %local.objectdata.attrib;"
+>
+<!ENTITY % local.videodata.attrib "">
+<!ENTITY % videodata.role.attrib "%role.attrib;">
+<!ELEMENT videodata EMPTY>
+<!ATTLIST videodata
+ width CDATA #IMPLIED
+ contentwidth CDATA #IMPLIED
+ depth CDATA #IMPLIED
+ contentdepth CDATA #IMPLIED
+ align (left
+ |right
+ |center) #IMPLIED
+ valign (top
+ |middle
+ |bottom) #IMPLIED
+ scale CDATA #IMPLIED
+ scalefit %yesorno.attvals;
+ #IMPLIED
+ %objectdata.attrib;
+ %common.attrib;
+ %videodata.role.attrib;
+ %local.videodata.attrib;
+>
+<!ENTITY % local.audiodata.attrib "">
+<!ENTITY % audiodata.role.attrib "%role.attrib;">
+<!ELEMENT audiodata EMPTY>
+<!ATTLIST audiodata
+ %objectdata.attrib;
+ %common.attrib;
+ %audiodata.role.attrib;
+ %local.audiodata.attrib;
+>
+<!ENTITY % local.imagedata.attrib "">
+<!ENTITY % imagedata.role.attrib "%role.attrib;">
+<!ELEMENT imagedata EMPTY>
+<!ATTLIST imagedata
+ width CDATA #IMPLIED
+ contentwidth CDATA #IMPLIED
+ depth CDATA #IMPLIED
+ contentdepth CDATA #IMPLIED
+ align (left
+ |right
+ |center) #IMPLIED
+ valign (top
+ |middle
+ |bottom) #IMPLIED
+ scale CDATA #IMPLIED
+ scalefit %yesorno.attvals;
+ #IMPLIED
+ %objectdata.attrib;
+ %common.attrib;
+ %imagedata.role.attrib;
+ %local.imagedata.attrib;
+>
+<!ENTITY % local.textdata.attrib "">
+<!ENTITY % textdata.role.attrib "%role.attrib;">
+<!ELEMENT textdata EMPTY>
+<!ATTLIST textdata
+ encoding CDATA #IMPLIED
+ %objectdata.attrib;
+ %common.attrib;
+ %textdata.role.attrib;
+ %local.textdata.attrib;
+>
+<!ENTITY % html.coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style CDATA #IMPLIED
+ title CDATA #IMPLIED"
+ >
+<!ENTITY % i18n
+ "xml:lang NMTOKEN #IMPLIED"
+ >
+<!ENTITY % events
+ "onclick CDATA #IMPLIED
+ ondblclick CDATA #IMPLIED
+ onmousedown CDATA #IMPLIED
+ onmouseup CDATA #IMPLIED
+ onmouseover CDATA #IMPLIED
+ onmousemove CDATA #IMPLIED
+ onmouseout CDATA #IMPLIED
+ onkeypress CDATA #IMPLIED
+ onkeydown CDATA #IMPLIED
+ onkeyup CDATA #IMPLIED"
+ >
+<!ENTITY % attrs "%html.coreattrs; %i18n; %events;">
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED"
+ >
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th (%para.char.mix; | %tabentry.mix;)*>
+<!ELEMENT td (%para.char.mix; | %tabentry.mix;)*>
+<!ATTLIST colgroup
+ %attrs;
+ span CDATA "1"
+ width CDATA #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+<!ATTLIST col
+ %attrs;
+ span CDATA "1"
+ width CDATA #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ bgcolor CDATA #IMPLIED
+ >
+<!ATTLIST th
+ %attrs;
+ abbr CDATA #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope (row|col|rowgroup|colgroup) #IMPLIED
+ rowspan CDATA "1"
+ colspan CDATA "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor CDATA #IMPLIED
+ width CDATA #IMPLIED
+ height CDATA #IMPLIED
+ >
+<!ATTLIST td
+ %attrs;
+ abbr CDATA #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope (row|col|rowgroup|colgroup) #IMPLIED
+ rowspan CDATA "1"
+ colspan CDATA "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor CDATA #IMPLIED
+ width CDATA #IMPLIED
+ height CDATA #IMPLIED
+ >
+<!ENTITY % tables.role.attrib "%role.attrib;">
+<!ENTITY % bodyatt "
+ floatstyle CDATA #IMPLIED
+ %label.attrib;"
+>
+<!ENTITY % secur "
+ %common.attrib;
+ class CDATA #IMPLIED
+ style CDATA #IMPLIED
+ title CDATA #IMPLIED
+ %i18n;
+ %events;
+ %tables.role.attrib;">
+<!ENTITY % common.table.attribs
+ "%bodyatt;
+ %secur;">
+<!ENTITY % tbl.table.att '
+ tabstyle CDATA #IMPLIED
+ tocentry %yesorno.attvals; #IMPLIED
+ shortentry %yesorno.attvals; #IMPLIED
+ orient (port|land) #IMPLIED
+ pgwide %yesorno.attvals; #IMPLIED
+ summary CDATA #IMPLIED
+ width CDATA #IMPLIED
+ border CDATA #IMPLIED
+ rules (none | groups | rows | cols | all) #IMPLIED
+ cellspacing CDATA #IMPLIED
+ cellpadding CDATA #IMPLIED
+ align (left|center|right) #IMPLIED
+ bgcolor CDATA #IMPLIED
+'>
+<!ENTITY % tbl.frame.attval "void|above|below|hsides|lhs|rhs|vsides|box|border|
+top|bottom|topbot|all|sides|none">
+<!ENTITY % tbl.entry.mdl "%para.char.mix; | %tabentry.mix;">
+<!ENTITY % tbl.hdft.mdl "(tr+|(colspec*,row+))">
+<!ENTITY % tbl.tbody.mdl "(tr+|row+)">
+<!ENTITY % yesorno 'CDATA'>
+<!ENTITY % tbl.tgroup.mdl "colspec*,spanspec*,thead?,tfoot?,tbody">
+<!ENTITY % tbl.tgroup.att '
+ tgroupstyle CDATA #IMPLIED '>
+<!ENTITY % tbl.row.mdl "(entry|entrytbl)+">
+<!ENTITY % tbl.entrytbl.mdl "colspec*,spanspec*,thead?,tbody">
+<!ELEMENT table (%tbl.table.mdl;)>
+<!ATTLIST table
+ frame (%tbl.frame.attval;) #IMPLIED
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ %tbl.table.att;
+ %bodyatt;
+ %secur;
+>
+<!ELEMENT tgroup (%tbl.tgroup.mdl;) >
+<!ATTLIST tgroup
+ cols CDATA #REQUIRED
+ %tbl.tgroup.att;
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ align (left|right|center|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED
+ %secur;
+>
+<!ELEMENT colspec EMPTY >
+<!ATTLIST colspec
+ colnum CDATA #IMPLIED
+ colname CDATA #IMPLIED
+ colwidth CDATA #IMPLIED
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ align (left|right|center|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED
+>
+<!ELEMENT spanspec EMPTY >
+<!ATTLIST spanspec
+ namest CDATA #REQUIRED
+ nameend CDATA #REQUIRED
+ spanname CDATA #REQUIRED
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ align (left|right|center|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED
+>
+<!ELEMENT thead (%tbl.hdft.mdl;)>
+<!ATTLIST thead
+ valign (top|middle|bottom) #IMPLIED
+ %secur;
+>
+<!ELEMENT tfoot (%tbl.hdft.mdl;)>
+<!ATTLIST tfoot
+ valign (top|middle|bottom) #IMPLIED
+ %secur;
+>
+<!ELEMENT tbody (%tbl.tbody.mdl;)>
+<!ATTLIST tbody
+ valign (top|middle|bottom) #IMPLIED
+ %secur;
+>
+<!ELEMENT row (%tbl.row.mdl;)>
+<!ATTLIST row
+ rowsep %yesorno; #IMPLIED
+ valign (top|middle|bottom) #IMPLIED
+ %secur;
+>
+<!ELEMENT entrytbl (%tbl.entrytbl.mdl;)>
+<!ATTLIST entrytbl
+ cols CDATA #REQUIRED
+ %tbl.tgroup.att;
+ colname CDATA #IMPLIED
+ spanname CDATA #IMPLIED
+ namest CDATA #IMPLIED
+ nameend CDATA #IMPLIED
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ align (left|right|center|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED
+ %secur;
+>
+<!ELEMENT entry (%tbl.entry.mdl;)*>
+<!ATTLIST entry
+ colname CDATA #IMPLIED
+ namest CDATA #IMPLIED
+ nameend CDATA #IMPLIED
+ spanname CDATA #IMPLIED
+ morerows CDATA #IMPLIED
+ colsep %yesorno; #IMPLIED
+ rowsep %yesorno; #IMPLIED
+ align (left|right|center|justify|char) #IMPLIED
+ char CDATA #IMPLIED
+ charoff CDATA #IMPLIED
+ rotate %yesorno; #IMPLIED
+ valign (top|middle|bottom) #IMPLIED
+ %secur;
+>
+<!ENTITY % local.informaltable.attrib "">
+<!ATTLIST informaltable
+ frame (%tbl.frame.attval;) #IMPLIED
+ colsep %yesorno.attvals; #IMPLIED
+ rowsep %yesorno.attvals; #IMPLIED
+ %common.table.attribs;
+ %tbl.table.att;
+ %local.informaltable.attrib;
+>
+<!ENTITY % local.caption.attrib "">
+<!ENTITY % caption.role.attrib "%role.attrib;">
+<!ELEMENT caption (#PCDATA | %textobject.mix;)*>
+<!ENTITY % caption.attlist.content "
+ %lang.attrib;
+ %remap.attrib;
+ %xreflabel.attrib;
+ %revisionflag.attrib;
+ %effectivity.attrib;
+ %dir.attrib;
+ %xml-base.attrib;
+ %local.common.attrib;
+ %caption.role.attrib;
+ %attrs;
+ align (top|bottom|left|right) #IMPLIED
+ %local.caption.attrib;
+">
+<!ATTLIST caption %caption.attlist.content;>
+<!ENTITY % local.affiliation.attrib "">
+<!ENTITY % affiliation.role.attrib "%role.attrib;">
+<!ATTLIST affiliation
+ %common.attrib;
+ %affiliation.role.attrib;
+ %local.affiliation.attrib;
+>
+<!ENTITY % local.jobtitle.attrib "">
+<!ENTITY % jobtitle.role.attrib "%role.attrib;">
+<!ELEMENT jobtitle (%docinfo.char.mix;)*>
+<!ATTLIST jobtitle
+ %common.attrib;
+ %jobtitle.role.attrib;
+ %local.jobtitle.attrib;
+>
+<!ENTITY % local.author.attrib "">
+<!ENTITY % author.role.attrib "%role.attrib;">
+<!ATTLIST author
+ %common.attrib;
+ %author.role.attrib;
+ %local.author.attrib;
+>
+<!ENTITY % local.authorgroup.attrib "">
+<!ENTITY % authorgroup.role.attrib "%role.attrib;">
+<!ATTLIST authorgroup
+ %common.attrib;
+ %authorgroup.role.attrib;
+ %local.authorgroup.attrib;
+>
+<!ENTITY % local.authorinitials.attrib "">
+<!ENTITY % authorinitials.role.attrib "%role.attrib;">
+<!ELEMENT authorinitials (%docinfo.char.mix;)*>
+<!ATTLIST authorinitials
+ %common.attrib;
+ %authorinitials.role.attrib;
+ %local.authorinitials.attrib;
+>
+<!ENTITY % local.copyright.attrib "">
+<!ENTITY % copyright.role.attrib "%role.attrib;">
+<!ELEMENT copyright (year+, holder*)>
+<!ATTLIST copyright
+ %common.attrib;
+ %copyright.role.attrib;
+ %local.copyright.attrib;
+>
+<!ENTITY % local.year.attrib "">
+<!ENTITY % year.role.attrib "%role.attrib;">
+<!ELEMENT year (%docinfo.char.mix;)*>
+<!ATTLIST year
+ %common.attrib;
+ %year.role.attrib;
+ %local.year.attrib;
+>
+<!ENTITY % local.holder.attrib "">
+<!ENTITY % holder.role.attrib "%role.attrib;">
+<!ELEMENT holder (%docinfo.char.mix;)*>
+<!ATTLIST holder
+ %common.attrib;
+ %holder.role.attrib;
+ %local.holder.attrib;
+>
+<!ENTITY % local.corpauthor.attrib "">
+<!ENTITY % corpauthor.role.attrib "%role.attrib;">
+<!ELEMENT corpauthor (%docinfo.char.mix;)*>
+<!ATTLIST corpauthor
+ %common.attrib;
+ %corpauthor.role.attrib;
+ %local.corpauthor.attrib;
+>
+<!ENTITY % local.date.attrib "">
+<!ENTITY % date.role.attrib "%role.attrib;">
+<!ELEMENT date (%docinfo.char.mix;)*>
+<!ATTLIST date
+ %common.attrib;
+ %date.role.attrib;
+ %local.date.attrib;
+>
+<!ENTITY % local.edition.attrib "">
+<!ENTITY % edition.role.attrib "%role.attrib;">
+<!ELEMENT edition (%docinfo.char.mix;)*>
+<!ATTLIST edition
+ %common.attrib;
+ %edition.role.attrib;
+ %local.edition.attrib;
+>
+<!ENTITY % local.editor.attrib "">
+<!ENTITY % editor.role.attrib "%role.attrib;">
+<!ATTLIST editor
+ %common.attrib;
+ %editor.role.attrib;
+ %local.editor.attrib;
+>
+<!ENTITY % local.issuenum.attrib "">
+<!ENTITY % issuenum.role.attrib "%role.attrib;">
+<!ELEMENT issuenum (%docinfo.char.mix;)*>
+<!ATTLIST issuenum
+ %common.attrib;
+ %issuenum.role.attrib;
+ %local.issuenum.attrib;
+>
+<!ENTITY % local.legalnotice.attrib "">
+<!ENTITY % legalnotice.role.attrib "%role.attrib;">
+<!ATTLIST legalnotice
+ %common.attrib;
+ %legalnotice.role.attrib;
+ %local.legalnotice.attrib;
+>
+<!ENTITY % local.orgname.attrib "">
+<!ENTITY % orgname.role.attrib "%role.attrib;">
+<!ELEMENT orgname (%docinfo.char.mix;)*>
+<!ATTLIST orgname
+ %common.attrib;
+ class (corporation|nonprofit|consortium|informal|other) #IMPLIED
+ otherclass CDATA #IMPLIED
+ %orgname.role.attrib;
+ %local.orgname.attrib;
+>
+<!ENTITY % local.othercredit.attrib "">
+<!ENTITY % othercredit.role.attrib "%role.attrib;">
+<!ATTLIST othercredit
+ class (graphicdesigner
+ |productioneditor
+ |copyeditor
+ |technicaleditor
+ |translator
+ |other) #IMPLIED
+ %common.attrib;
+ %othercredit.role.attrib;
+ %local.othercredit.attrib;
+>
+<!ENTITY % local.firstname.attrib "">
+<!ENTITY % firstname.role.attrib "%role.attrib;">
+<!ELEMENT firstname (%docinfo.char.mix;)*>
+<!ATTLIST firstname
+ %common.attrib;
+ %firstname.role.attrib;
+ %local.firstname.attrib;
+>
+<!ENTITY % local.honorific.attrib "">
+<!ENTITY % honorific.role.attrib "%role.attrib;">
+<!ELEMENT honorific (%docinfo.char.mix;)*>
+<!ATTLIST honorific
+ %common.attrib;
+ %honorific.role.attrib;
+ %local.honorific.attrib;
+>
+<!ENTITY % local.lineage.attrib "">
+<!ENTITY % lineage.role.attrib "%role.attrib;">
+<!ELEMENT lineage (%docinfo.char.mix;)*>
+<!ATTLIST lineage
+ %common.attrib;
+ %lineage.role.attrib;
+ %local.lineage.attrib;
+>
+<!ENTITY % local.othername.attrib "">
+<!ENTITY % othername.role.attrib "%role.attrib;">
+<!ELEMENT othername (%docinfo.char.mix;)*>
+<!ATTLIST othername
+ %common.attrib;
+ %othername.role.attrib;
+ %local.othername.attrib;
+>
+<!ENTITY % local.surname.attrib "">
+<!ENTITY % surname.role.attrib "%role.attrib;">
+<!ELEMENT surname (%docinfo.char.mix;)*>
+<!ATTLIST surname
+ %common.attrib;
+ %surname.role.attrib;
+ %local.surname.attrib;
+>
+<!ENTITY % local.pubdate.attrib "">
+<!ENTITY % pubdate.role.attrib "%role.attrib;">
+<!ELEMENT pubdate (%docinfo.char.mix;)*>
+<!ATTLIST pubdate
+ %common.attrib;
+ %pubdate.role.attrib;
+ %local.pubdate.attrib;
+>
+<!ENTITY % local.publishername.attrib "">
+<!ENTITY % publishername.role.attrib "%role.attrib;">
+<!ELEMENT publishername (%docinfo.char.mix;)*>
+<!ATTLIST publishername
+ %common.attrib;
+ %publishername.role.attrib;
+ %local.publishername.attrib;
+>
+<!ENTITY % local.releaseinfo.attrib "">
+<!ENTITY % releaseinfo.role.attrib "%role.attrib;">
+<!ELEMENT releaseinfo (%docinfo.char.mix;)*>
+<!ATTLIST releaseinfo
+ %common.attrib;
+ %releaseinfo.role.attrib;
+ %local.releaseinfo.attrib;
+>
+<!ENTITY % local.revhistory.attrib "">
+<!ENTITY % revhistory.role.attrib "%role.attrib;">
+<!ELEMENT revhistory (revision+)>
+<!ATTLIST revhistory
+ %common.attrib;
+ %revhistory.role.attrib;
+ %local.revhistory.attrib;
+>
+<!ENTITY % local.revision.attrib "">
+<!ENTITY % revision.role.attrib "%role.attrib;">
+<!ELEMENT revision (revnumber, date, (author|authorinitials)*,
+ (revremark|revdescription)?)>
+<!ATTLIST revision
+ %common.attrib;
+ %revision.role.attrib;
+ %local.revision.attrib;
+>
+<!ENTITY % local.revnumber.attrib "">
+<!ENTITY % revnumber.role.attrib "%role.attrib;">
+<!ELEMENT revnumber (%docinfo.char.mix;)*>
+<!ATTLIST revnumber
+ %common.attrib;
+ %revnumber.role.attrib;
+ %local.revnumber.attrib;
+>
+<!ENTITY % local.revremark.attrib "">
+<!ENTITY % revremark.role.attrib "%role.attrib;">
+<!ELEMENT revremark (%docinfo.char.mix;)*>
+<!ATTLIST revremark
+ %common.attrib;
+ %revremark.role.attrib;
+ %local.revremark.attrib;
+>
+<!ENTITY % local.revdescription.attrib "">
+<!ENTITY % revdescription.role.attrib "%role.attrib;">
+<!ELEMENT revdescription ((%revdescription.mix;)+)>
+<!ATTLIST revdescription
+ %common.attrib;
+ %revdescription.role.attrib;
+ %local.revdescription.attrib;
+>
+<!ENTITY % local.volumenum.attrib "">
+<!ENTITY % volumenum.role.attrib "%role.attrib;">
+<!ELEMENT volumenum (%docinfo.char.mix;)*>
+<!ATTLIST volumenum
+ %common.attrib;
+ %volumenum.role.attrib;
+ %local.volumenum.attrib;
+>
+<!ENTITY % local.command.attrib "">
+<!ENTITY % command.role.attrib "%role.attrib;">
+<!ELEMENT command (%cptr.char.mix;)*>
+<!ATTLIST command
+ %moreinfo.attrib;
+ %common.attrib;
+ %command.role.attrib;
+ %local.command.attrib;
+>
+<!ENTITY % local.computeroutput.attrib "">
+<!ENTITY % computeroutput.role.attrib "%role.attrib;">
+<!ATTLIST computeroutput
+ %moreinfo.attrib;
+ %common.attrib;
+ %computeroutput.role.attrib;
+ %local.computeroutput.attrib;
+>
+<!ENTITY % local.email.attrib "">
+<!ENTITY % email.role.attrib "%role.attrib;">
+<!ELEMENT email (%docinfo.char.mix;)*>
+<!ATTLIST email
+ %common.attrib;
+ %email.role.attrib;
+ %local.email.attrib;
+>
+<!ENTITY % local.filename.attrib "">
+<!ENTITY % filename.role.attrib "%role.attrib;">
+<!ELEMENT filename (%cptr.char.mix;)*>
+<!ATTLIST filename
+ class (headerfile
+ |partition
+ |devicefile
+ |libraryfile
+ |directory
+ |extension
+ |symlink) #IMPLIED
+ path CDATA #IMPLIED
+ %moreinfo.attrib;
+ %common.attrib;
+ %filename.role.attrib;
+ %local.filename.attrib;
+>
+<!ENTITY % local.lineannotation.attrib "">
+<!ENTITY % lineannotation.role.attrib "%role.attrib;">
+<!ELEMENT lineannotation (%para.char.mix;)*>
+<!ATTLIST lineannotation
+ %common.attrib;
+ %lineannotation.role.attrib;
+ %local.lineannotation.attrib;
+>
+<!ENTITY % local.literal.attrib "">
+<!ENTITY % literal.role.attrib "%role.attrib;">
+<!ELEMENT literal (%cptr.char.mix;)*>
+<!ATTLIST literal
+ %moreinfo.attrib;
+ %common.attrib;
+ %literal.role.attrib;
+ %local.literal.attrib;
+>
+<!ENTITY % local.option.attrib "">
+<!ENTITY % option.role.attrib "%role.attrib;">
+<!ELEMENT option (%cptr.char.mix;)*>
+<!ATTLIST option
+ %common.attrib;
+ %option.role.attrib;
+ %local.option.attrib;
+>
+<!ENTITY % local.replaceable.attrib "">
+<!ENTITY % replaceable.role.attrib "%role.attrib;">
+<!ATTLIST replaceable
+ class (command
+ |function
+ |option
+ |parameter) #IMPLIED
+ %common.attrib;
+ %replaceable.role.attrib;
+ %local.replaceable.attrib;
+>
+<!ENTITY % local.systemitem.attrib "">
+<!ENTITY % systemitem.role.attrib "%role.attrib;">
+<!ATTLIST systemitem
+ class (constant
+ |event
+ |eventhandler
+ |domainname
+ |fqdomainname
+ |ipaddress
+ |netmask
+ |etheraddress
+ |groupname
+ |library
+ |macro
+ |osname
+ |filesystem
+ |resource
+ |systemname
+ |username
+ |newsgroup
+ |process
+ |service
+ |server
+ |daemon) #IMPLIED
+ %moreinfo.attrib;
+ %common.attrib;
+ %systemitem.role.attrib;
+ %local.systemitem.attrib;
+>
+<!ENTITY % local.userinput.attrib "">
+<!ENTITY % userinput.role.attrib "%role.attrib;">
+<!ATTLIST userinput
+ %moreinfo.attrib;
+ %common.attrib;
+ %userinput.role.attrib;
+ %local.userinput.attrib;
+>
+<!ENTITY % local.abbrev.attrib "">
+<!ENTITY % abbrev.role.attrib "%role.attrib;">
+<!ELEMENT abbrev (%word.char.mix;)*>
+<!ATTLIST abbrev
+ %common.attrib;
+ %abbrev.role.attrib;
+ %local.abbrev.attrib;
+>
+<!ENTITY % local.acronym.attrib "">
+<!ENTITY % acronym.role.attrib "%role.attrib;">
+<!ELEMENT acronym (%word.char.mix;)*
+ >
+<!ATTLIST acronym
+ %common.attrib;
+ %acronym.role.attrib;
+ %local.acronym.attrib;
+>
+<!ENTITY % local.citetitle.attrib "">
+<!ENTITY % citetitle.role.attrib "%role.attrib;">
+<!ELEMENT citetitle (%para.char.mix;)*>
+<!ATTLIST citetitle
+ pubwork (article
+ |book
+ |chapter
+ |part
+ |refentry
+ |section
+ |journal
+ |series
+ |set
+ |manuscript
+ |cdrom
+ |dvd
+ |wiki
+ |gopher
+ |bbs
+ |emailmessage
+ |webpage
+ |newsposting) #IMPLIED
+ %common.attrib;
+ %citetitle.role.attrib;
+ %local.citetitle.attrib;
+>
+<!ENTITY % local.emphasis.attrib "">
+<!ENTITY % emphasis.role.attrib "%role.attrib;">
+<!ELEMENT emphasis (%para.char.mix;)*>
+<!ATTLIST emphasis
+ %common.attrib;
+ %emphasis.role.attrib;
+ %local.emphasis.attrib;
+>
+<!ENTITY % local.phrase.attrib "">
+<!ENTITY % phrase.role.attrib "%role.attrib;">
+<!ELEMENT phrase (%para.char.mix;)*>
+<!ATTLIST phrase
+ %common.attrib;
+ %phrase.role.attrib;
+ %local.phrase.attrib;
+>
+<!ENTITY % local.quote.attrib "">
+<!ENTITY % quote.role.attrib "%role.attrib;">
+<!ELEMENT quote (%para.char.mix;)*>
+<!ATTLIST quote
+ %common.attrib;
+ %quote.role.attrib;
+ %local.quote.attrib;
+>
+<!ENTITY % local.ssscript.attrib "">
+<!ENTITY % ssscript.role.attrib "%role.attrib;">
+<!ATTLIST subscript
+ %common.attrib;
+ %ssscript.role.attrib;
+ %local.ssscript.attrib;
+>
+<!ATTLIST superscript
+ %common.attrib;
+ %ssscript.role.attrib;
+ %local.ssscript.attrib;
+>
+<!ENTITY % local.trademark.attrib "">
+<!ENTITY % trademark.role.attrib "%role.attrib;">
+<!ATTLIST trademark
+ class (service
+ |trade
+ |registered
+ |copyright) 'trade'
+ %common.attrib;
+ %trademark.role.attrib;
+ %local.trademark.attrib;
+>
+<!ENTITY % local.link.attrib "">
+<!ENTITY % link.role.attrib "%role.attrib;">
+<!ELEMENT link (%para.char.mix;)*
+ >
+<!ATTLIST link
+ endterm IDREF #IMPLIED
+ xrefstyle CDATA #IMPLIED
+ type CDATA #IMPLIED
+ %linkendreq.attrib;
+ %common.attrib;
+ %link.role.attrib;
+ %local.link.attrib;
+>
+<!ENTITY % local.ulink.attrib "">
+<!ENTITY % ulink.role.attrib "%role.attrib;">
+<!ELEMENT ulink (%para.char.mix;)*
+ >
+<!ATTLIST ulink
+ url CDATA #REQUIRED
+ type CDATA #IMPLIED
+ xrefstyle CDATA #IMPLIED
+ %common.attrib;
+ %ulink.role.attrib;
+ %local.ulink.attrib;
+>
+<!ENTITY % local.footnoteref.attrib "">
+<!ENTITY % footnoteref.role.attrib "%role.attrib;">
+<!ELEMENT footnoteref EMPTY>
+<!ATTLIST footnoteref
+ %linkendreq.attrib; %label.attrib;
+ %common.attrib;
+ %footnoteref.role.attrib;
+ %local.footnoteref.attrib;
+>
+<!ENTITY % local.xref.attrib "">
+<!ENTITY % xref.role.attrib "%role.attrib;">
+<!ELEMENT xref EMPTY>
+<!ATTLIST xref
+ endterm IDREF #IMPLIED
+ xrefstyle CDATA #IMPLIED
+ %common.attrib;
+ %linkendreq.attrib;
+ %xref.role.attrib;
+ %local.xref.attrib;
+>
+<!ENTITY % local.anchor.attrib "">
+<!ENTITY % anchor.role.attrib "%role.attrib;">
+<!ELEMENT anchor EMPTY>
+<!ATTLIST anchor
+ %idreq.attrib; %pagenum.attrib; %remap.attrib;
+ %xreflabel.attrib;
+ %revisionflag.attrib;
+ %effectivity.attrib;
+ %anchor.role.attrib;
+ %local.anchor.attrib;
+>
+<!ENTITY % local.appendix.class "">
+<!ENTITY % appendix.class "appendix %local.appendix.class;">
+<!ENTITY % div.title.content
+ "title, subtitle?, titleabbrev?">
+<!ENTITY % bookcomponent.title.content
+ "title, subtitle?, titleabbrev?">
+<!ENTITY % sect.title.content
+ "title, subtitle?, titleabbrev?">
+<!ENTITY % local.appendix.attrib "">
+<!ENTITY % appendix.role.attrib "%role.attrib;">
+<!ATTLIST appendix
+ %label.attrib;
+ %status.attrib;
+ %common.attrib;
+ %appendix.role.attrib;
+ %local.appendix.attrib;
+>
+<!ENTITY % local.section.attrib "">
+<!ENTITY % section.role.attrib "%role.attrib;">
+<!ATTLIST section
+ %label.attrib;
+ %status.attrib;
+ %common.attrib;
+ %section.role.attrib;
+ %local.section.attrib;
+>
+<!ENTITY % local.bibliography.attrib "">
+<!ENTITY % bibliography.role.attrib "%role.attrib;">
+<!ATTLIST bibliography
+ %status.attrib;
+ %common.attrib;
+ %bibliography.role.attrib;
+ %local.bibliography.attrib;
+>
+<!ENTITY % local.bibliodiv.attrib "">
+<!ENTITY % bibliodiv.role.attrib "%role.attrib;">
+<!ATTLIST bibliodiv
+ %status.attrib;
+ %common.attrib;
+ %bibliodiv.role.attrib;
+ %local.bibliodiv.attrib;
+>
+<!ENTITY % local.article.attrib "">
+<!ENTITY % article.role.attrib "%role.attrib;">
+<!ATTLIST article
+ class (journalarticle
+ |productsheet
+ |whitepaper
+ |techreport
+ |specification
+ |faq) #IMPLIED
+ parentbook IDREF #IMPLIED
+ %status.attrib;
+ %common.attrib;
+ %article.role.attrib;
+ %local.article.attrib;
+>
+<!ELEMENT objectinfo ((mediaobject | legalnotice
+ | keywordset | subjectset | %bibliocomponent.mix;)+)>
+<!ELEMENT section (sectioninfo?,
+ (%sect.title.content;),
+ (((%divcomponent.mix;)+, section*)
+ | section+))>
+<!ELEMENT sectioninfo ((mediaobject | legalnotice
+ | keywordset | subjectset | %bibliocomponent.mix;)+)>
+<!ELEMENT authorgroup ((author|editor|corpauthor|othercredit)+)>
+<!ELEMENT author ((%person.ident.mix;)+)>
+<!ELEMENT editor ((%person.ident.mix;)+)>
+<!ELEMENT othercredit ((%person.ident.mix;)+)>
+<!ELEMENT affiliation (jobtitle?, orgname?)>
+<!ELEMENT para (%para.char.mix;)*>
+<!ENTITY % informaltable.table.mdl
+ "((mediaobject+|tgroup+)
+ |(caption, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+)))">
+<!ELEMENT informaltable (%informaltable.table.mdl;) >
+<!ELEMENT replaceable (#PCDATA
+ | %link.char.class;
+ | %base.char.class;
+ | inlinemediaobject)*>
+<!ELEMENT trademark (#PCDATA
+ | %link.char.class;
+ | %tech.char.class;
+ | inlinemediaobject
+ | emphasis)*>
+<!ELEMENT article ((%div.title.content;)?, articleinfo?,
+ (%bookcomponent.content;),
+ ((%appendix.class;)|bibliography)*)>
+<!ELEMENT articleinfo ((mediaobject | legalnotice
+ | subjectset | keywordset | %bibliocomponent.mix;)+)>
+<!ELEMENT appendix ((%bookcomponent.title.content;),
+ (%bookcomponent.content;))>
+<!ELEMENT bibliography ((%bookcomponent.title.content;)?,
+ (%component.mix;)*,
+ (bibliodiv+ | bibliomixed+))>
+<!ELEMENT bibliomixed (#PCDATA | %bibliocomponent.mix; | bibliomset)*>
+<!ELEMENT bibliodiv ((%sect.title.content;)?, (%component.mix;)*,
+ (bibliomixed)+)>
+<!ELEMENT sidebar ((%formalobject.title.content;)?,
+ (%sidebar.mix;)+)>
+<!ELEMENT programlisting (%para.char.mix; | lineannotation)*>
+<!ELEMENT literallayout (%para.char.mix; | lineannotation)*>
+<!ELEMENT itemizedlist ((%formalobject.title.content;)?,
+ (%listpreamble.mix;)*, listitem+)>
+<!ELEMENT orderedlist ((%formalobject.title.content;)?,
+ (%listpreamble.mix;)*, listitem+)>
+<!ELEMENT variablelist ((%formalobject.title.content;)?,
+ (%listpreamble.mix;)*, varlistentry+)>
+<!ELEMENT example ((%formalobject.title.content;), (%example.mix;)+)
+ >
+<!ELEMENT figure ((%formalobject.title.content;),
+ (%figure.mix; | %link.char.class;)+)>
+<!ELEMENT legalnotice (title?, (%legalnotice.mix;)+)
+ >
+<!ELEMENT systemitem (%cptr.char.mix; | acronym)*>
+<!ELEMENT computeroutput (%cptr.char.mix;)*>
+<!ELEMENT userinput (%cptr.char.mix;)*>
+<!ELEMENT blockquote (title?, attribution?, (%component.mix;)+)>
+<!ELEMENT subscript (#PCDATA
+ | %link.char.class;
+ | emphasis
+ | replaceable
+ | inlinemediaobject
+ | %base.char.class;
+ | %other.char.class;)*
+ >
+<!ELEMENT superscript (#PCDATA
+ | %link.char.class;
+ | emphasis
+ | replaceable
+ | inlinemediaobject
+ | %base.char.class;
+ | %other.char.class;)*
+ >
--- /dev/null
+#
+# OBSOLETE
+#
+
--- /dev/null
+<!-- $Id: examples.xml,v 1.3 2007/05/11 08:16:44 thuffir Exp $ -->
+
+<section>
+<title>Examples of How to Create Documentation</title>
+
+<section>
+<title>HTML Example</title>
+ <para> For this you need a web browser, say FireFox or Mozilla. You can try
+ this in the robodoc root directory. It creates a document called
+ <filename>HDocs/masterindex.html</filename> plus a lot of smaller
+ documents from all the source files in the directory
+ <filename>Source</filename>.</para>
+
+<programlisting>
+<command>robodoc</command> <option>--src</option> ./Source <option>--doc</option> ./HDocs <option>--multidoc</option> <option>--index</option> <option>--html</option>
+</programlisting>
+</section>
+
+<section>
+<title>RTF Example</title>
+
+ <para>For this you need an rtf reader, for instance
+ <command>Word</command>. You can try this in the robodoc root
+ directory.</para>
+
+<programlisting>
+<command>robodoc</command> <option>--src</option> ./Source <option>--doc</option> api <option>--singledoc</option> <option>--rtf</option> <option>--sections</option>
+</programlisting>
+
+ <para>This will create a document called
+ <filename>api.rtf</filename>.</para>
+
+ <para>By default the document looks pretty plain. There is no
+ chapter numbering or a table of contents, even if you asked for
+ it. All the information for this is included but not visible.
+ This is because chapter numbering and a table of contents are
+ generated by Word based on formatting information that is part of
+ a Word document but not part of a RTF document. </para>
+
+ <para>To make it visible you include the generated document into a
+ bigger document with the right formatting options. This is best
+ done with a cut-and-paste operation. Use the cut-past-paste
+ special menu, and paste it as RTF formatted text into your Word
+ document.</para>
+
+</section>
+
+<section>
+<title>LaTeX Example</title>
+
+ <para> For this you need <command>latex</command> and
+ <command>makeindex</command>. You can try this in the robodoc root
+ directory. It creates a single document called
+ <filename>api.dvi</filename> from all the source files in the
+ directory Source.</para>
+
+<programlisting>
+<command>robodoc</command> <option>--src</option> ./Source <option>--doc</option> api <option>--singledoc</option> <option>--latex</option> <option>--sections</option>
+<command>latex</command> api.tex
+<command>latex</command> api.tex
+<command>makeindex</command> api.idx
+<command>latex</command> api.tex
+<command>xdvi</command> api.dvi
+</programlisting>
+</section>
+
+<section>
+<title>XML DocBook Example</title>
+
+<para>
+ DocBook is a xml format to create technical documentation, see.
+ <ulink url="http://www.docbook.org/"><citetitle>DocBook.org</citetitle></ulink>.
+ DocBook is quite nice. This manual for instance is written in DocBook and
+ automatically translated into html and pdf format.
+</para>
+
+<para>
+ You can use the DocBook output of ROBODoc to create several other formats,
+ for instance: html, pdf, html-help.
+ For this you need a tool that can process a DocBook file. There
+ are several of these tools.
+</para>
+
+<section>
+ <title>DocBook with html output</title>
+
+<para>The easiest to use is <command>xsltproc</command>. It works under
+ Windows and Unix. A typical workflow under Windows is:
+</para>
+
+<programlisting>
+<command>robodoc</command> <option>--src</option> ./Source <option>--doc</option> api <option>--singledoc</option> <option>--dbxml</option> <option>--sections</option>
+<command>xsltproc</command> api.xsl api.xml > api.html
+</programlisting>
+
+<para>
+ Where <filename>api.xsl</filename> contains:
+</para>
+
+<literallayout class="monospaced"><![CDATA[
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/html/docbook.xsl"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+]]>
+</literallayout>
+
+<para>For this you need <command>xsltproc</command>. For Windows these can be found at
+ <ulink url="http://www.zlatkovic.com/libxml.en.html">
+ <citetitle>http://www.zlatkovic.com libxml.en.html</citetitle></ulink>,
+ and the stylesheets which can be found at
+ <ulink url="http://docbook.sourceforge.net/"><citetitle>http://docbook.sourceforge.net/</citetitle></ulink>.
+ In the example above the style sheets are installed on <filename>e:/docbook/</filename>.
+</para>
+
+<para>
+ More information about xsl can be found at
+ <ulink url="http://www.sagehill.net/docbookxsl/"><citetitle>http://www.sagehill.net/docbookxsl/</citetitle></ulink>.
+</para>
+
+</section>
+
+<section>
+ <title>DocBook with html help output</title>
+
+<para>The same program can be used to
+ <ulink
+ url="http://docbook.sourceforge.net/release/xsl/current/doc/htmlhelp.html">
+ <citetitle>create a html help</citetitle>
+ </ulink> file. For this you need
+ <ulink
+ url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp">
+ <citetitle>HTML Help Workshop</citetitle>
+ </ulink>. The workflow now is:
+</para>
+
+<programlisting>
+<command>robodoc</command> <option>--src</option> ./Source <option>--doc</option> api <option>--singledoc</option> <option>--dbxml</option> <option>--sections</option>
+<command>xsltproc</command> api.xsl api.xml
+<command>hhc</command> htmlhelp.hhp
+</programlisting>
+
+<para>
+ Where <filename>api.xsl</filename> contains:
+</para>
+
+<literallayout class="monospaced"><![CDATA[
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/htmlhelp/htmlhelp.xsl"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+]]>
+</literallayout>
+
+</section>
+
+
+</section>
+</section>
+
--- /dev/null
+<section>
+<title>Extracting Documentation with ROBODoc</title>
+
+ <para>Now that you have prepared your source code for use with
+ ROBODoc you are ready to extract the documentation. There are
+ several choices to be made.</para>
+
+<section>
+<title>Single document or many smaller documents</title>
+
+ <para>First of all, ROBODoc can be used in three modes.</para>
+
+ <itemizedlist>
+
+ <listitem><para>multidoc -- in this mode ROBODoc scans
+ all the source files in your source directory and creates a
+ separate document file for each of these in a document directory.
+ The document directory is created automatically. Its structure is
+ a mirror of the structure of your source directory.</para></listitem>
+
+ <listitem><para>singledoc -- in this mode ROBODoc scans all the source
+ files in your source directory and creates a single documentation
+ file that contains all the documentation extracted from your
+ source files. </para></listitem>
+
+ <listitem><para>singlefile -- in this mode ROBODoc scans a single source
+ file and creates a single documentation file.</para></listitem>
+
+ </itemizedlist>
+</section>
+
+<section>
+<title>multidoc</title>
+
+ <para>The multidoc mode is useful to create browsable documents.
+ For instance many small HTML files that can be viewed with a
+ web-browser. This mode requires the following arguments:</para>
+
+ <para>
+ <command>robodoc
+ --src <replaceable>source directory</replaceable>
+ --doc <replaceable>document directory</replaceable>
+ --multidoc
+ other options
+ </command>
+</para>
+
+ <para>An additional option that is useful with this mode is
+ <option>--index</option>, this creates a series of index files,
+ one for each header type.</para>
+
+</section>
+
+
+<section>
+<title>singledoc</title>
+
+ <para> The singledoc mode is useful to create bulk documentation
+ that can be incorporated in other documents, or that can be
+ delivered to a client as a single document. For instance a file
+ created in RTF format can be included into a larger design
+ document written in Word format. This mode requires the following
+ arguments:</para>
+
+<para>
+ <command>robodoc
+ --src <replaceable>source directory</replaceable>
+ --doc <replaceable>document file without extension</replaceable>
+ --singledoc
+ <replaceable>other options</replaceable>
+ </command>
+</para>
+
+ <para>An additional option that is useful with this mode is
+ <option>--sections</option>, this causes the headers to follow a
+ section layout based on the module element hierarchy defined in the
+ header name.</para>
+
+</section>
+
+
+<section>
+<title>singlefile</title>
+
+ <para>The singlefile mode is not very useful. It is mainly used
+ for debugging purposes. This mode requires the following
+ arguments:</para>
+
+<para>
+ <command>robodoc
+ --src <replaceable>source file</replaceable>
+ --doc <replaceable>document file</replaceable>
+ --singlefile
+ other options
+</command>
+</para>
+
+</section>
+
+<section>
+<title>Output formats</title>
+
+ <para>Your next choice is the output format. ROBODoc can create
+ documentation in several formats:</para>
+
+ <itemizedlist>
+ <listitem><para>HTML, option <option>--html</option></para></listitem>
+ <listitem><para>RTF, option <option>--rtf</option></para></listitem>
+ <listitem><para>LaTeX, option <option>--latex</option></para></listitem>
+ <listitem><para>XML DocBook, option <option>--dbxml</option></para></listitem>
+ </itemizedlist>
+
+ <para>What format to use depends on your wishes. If you want a
+ single printable document, use LaTeX or XML DocBook. If you want
+ a document that can be included into a larger (Word) document use
+ RTF. If you want something that is browsable use HTML, or use XML
+ DocBook and then convert it to HTML.</para>
+
+</section>
+</section>
+
--- /dev/null
+<!-- vi:ft=html
+-->
+
+<c>General
+<q>What problem does ROBODoc solve?
+<a>TODO
+
+<c>General
+<q>What is the target audience for ROBODoc?
+<a>TODO
+
+<c>General
+<q>What are the possible uses for ROBODoc?
+<a>TODO
+
+<c>General
+<q>On what platforms foes ROBODoc run?
+<a>Win32, Unix, GNU/Linux, and Mac. But it probably works on
+other OS-es that have a C compiler too.
+
+<c>General
+<q>What programming languages are supported?
+<a>TODO
+
+<c>General
+<q>What human languages are supported?
+<a>TODO
+
+<c>General
+<q>Where can I download ROBODoc?
+<a>TODO
+
+<c>General
+<q>Are there other similar tools that are better?
+<a>TODO
+
+<!-- Theory --!>
+
+<c>Theory
+<q>What are headers
+<a>TODO
+
+<c>Theory
+<q>What are headertypes
+<a>TODO
+
+<c>Theory
+<q>What are items
+<a>TODO
+
+<c>Theory
+<q>Do item names need to be all captitals?
+<a>No. They can be anything you like, but they
+can not be followed by other text. You can
+define your itemnames in the ROBODoc configuration
+file <code>robodoc.rc</code>
+
+<!-- USE --!>
+
+<c>Use
+<q>Why should I use headertypes
+<a>TODO
+
+<!-- Configuration --!>
+
+<c>Configuration
+<q>How can I define my own items?
+<a>TODO
+
+<c>Configuration
+<q>How can I define my own headertypes?
+<a>TODO
+
--- /dev/null
+<hr>
+
+<p>Copyright (c) 2004 Frans Slothouber</p>
+
+<p><i>This list of questions and answers was generated by
+<a href="http://www.makefaq.org/">makefaq</a>.</i>
+
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+
+<!-- Change this title to something more appropriate for your FAQ. You
+ do NOT need to leave the reference to makefaq.py in there. -->
+
+<title>ROBODoc Frequently-Asked Questions</title>
+
+<!-- If you do not want to use a CSS stylesheet, remove this next line. -->
+
+<link type="text/css" rel="stylesheet" href="faq.css">
+
+</head>
+<body bgcolor="#FFFFFF">
+
+<h1>
+<!-- Change this text line below to something more appropriate -->
+<code>ROBODoc</code> Frequently-Asked Questions
+
+</h1>
+
+<!-- Change this line to have your contact information in it. -->
+<p>For more information about this faq, please contact <b>Frans Slothouber</b>
+
+<hr>
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use IO::File;
+
+
+#------------------------------------------
+#
+my @header_specs = (
+ { language => [ 'C' ],
+ begin => '/****',
+ mid => '*',
+ end_1 => '****',
+ end_2 => '/****',
+ rem_begin => '/*',
+ rem_end => '*/'
+ },
+
+ { language => [ 'Modula-2' ],
+ begin => '(****',
+ mid => '*',
+ end_1 => '****',
+ end_2 => '(****',
+ rem_begin => '(*',
+ rem_end => '*)'
+ },
+
+ { language => [ 'Pascal' ],
+ begin => '{****',
+ mid => '*',
+ end_1 => '****',
+ end_2 => '{****',
+ rem_begin => '{*',
+ rem_end => '*}'
+ },
+
+ { language => [ 'C++' ],
+ begin => '//****',
+ mid => '//',
+ end_2 => '//****',
+ },
+
+ { language => [ 'Tcl', 'Perl' ],
+ begin => '#****',
+ mid => '#',
+ end_2 => '#****',
+ },
+
+ { language => [ 'LaTeX/TeX', 'Postscript' ],
+ begin => '%****',
+ mid => '%',
+ end_2 => '%****',
+ },
+
+ { language => [ 'Occam' ],
+ begin => '__****',
+ mid => '__',
+ end_2 => '__****',
+ },
+
+
+);
+
+#--------------------------------------------------------------------
+#
+
+
+sub make_robodoc_rc_file {
+ my $spec = shift;
+ my $rc_file = "";
+
+ $rc_file .= "header markers:\n";
+ $rc_file .= " $spec->{begin}\n";
+ $rc_file .= "remark markers:\n";
+ $rc_file .= " $spec->{mid}\n";
+ $rc_file .= "end markers:\n";
+ if ( exists( $spec->{end_1} ) ) {
+ $rc_file .= " $spec->{end_1}\n";
+ }
+ if ( exists( $spec->{end_2} ) ) {
+ $rc_file .= " $spec->{end_2}\n";
+ }
+
+ if ( exists( $spec->{rem_end} ) ) {
+ $rc_file .= "remark begin markers:\n";
+ $rc_file .= " $spec->{rem_begin}\n";
+ $rc_file .= "remark end markers:\n";
+ $rc_file .= " $spec->{rem_end}\n";
+ }
+
+ return $rc_file;
+}
+
+
+#
+# given a header spec this returns an example header
+# that does not contain any source items.
+#
+sub make_example_header_simple {
+ my $spec = shift;
+ my $header = "";
+
+ $header = $spec->{begin} . "f* ModuleName/Foo\n";
+ $header .= $spec->{mid} . " FUNCTION\n";
+ $header .= $spec->{mid} . " Foo computes the foo factor\n";
+ $header .= $spec->{mid} . " using a fudge factor.\n";
+ if ( exists( $spec->{end_1} ) ) {
+ $header .= $spec->{end_1} . "\n";
+ $header .= $spec->{rem_end} . "\n";
+ } else {
+ $header .= $spec->{end_2} . "\n";
+ }
+}
+
+
+sub make_example_header {
+ my $spec = shift;
+ my $header = "";
+
+ $header = $spec->{begin} . "f* ModuleName/Foo\n";
+ $header .= $spec->{mid} . " FUNCTION\n";
+ $header .= $spec->{mid} . " Foo computes the foo factor\n";
+ $header .= $spec->{mid} . " using a fudge factor.\n";
+ $header .= $spec->{mid} . " SYNOPSIS\n";
+ if ( exists( $spec->{rem_end} ) ) {
+ $header .= $spec->{rem_end} . "\n";
+ } else {
+
+ }
+ $header .= "int Foo( int fudge )\n";
+ if ( exists( $spec->{rem_begin} ) ) {
+ $header .= $spec->{rem_begin} . "\n";
+ } else {
+
+ }
+ $header .= $spec->{mid} . " INPUTS\n";
+ $header .= $spec->{mid} . " fudge -- the fudge factor\n";
+ $header .= $spec->{mid} . " SOURCE\n";
+ if ( exists( $spec->{rem_end} ) ) {
+ $header .= $spec->{rem_end} . "\n";
+ } else {
+
+ }
+
+ $header .= "\n more source code..\n\n";
+
+ if ( exists( $spec->{rem_end} ) ) {
+ $header .= $spec->{end_2};
+ $header .= $spec->{rem_end} . "\n";
+ } else {
+ $header .= $spec->{end_2} . "\n";
+ }
+
+ return $header;
+}
+
+
+
+
+sub make_examples_for_manual {
+ my $filename = "header_examples.xml";
+ my $file = IO::File->new(">$filename") or die "$filename : $!";
+ foreach my $spec ( @header_specs ) {
+ foreach my $language ( @{$spec->{language}} ) {
+ print $file "<section>\n";
+ print $file "<title>$language</title>\n";
+ print $file "\n\n";
+
+ print $file "<example>\n";
+ print $file "<title>A simple header without any source items in $language.</title>\n";
+ print $file "<programlisting>\n";
+ print $file make_example_header_simple( $spec );
+ print $file "</programlisting>\n";
+ print $file "</example>\n";
+ print $file "\n\n";
+
+ print $file "<example>\n";
+ print $file "<title>A header with source items in $language.</title>\n";
+ print $file "<programlisting>\n";
+ print $file make_example_header( $spec );
+ print $file "</programlisting>\n";
+ print $file "</example>\n";
+ print $file "\n\n";
+
+ print $file "<example>\n";
+ print $file "<title>The robodoc.rc file required for $language if it were not supported by default.</title>\n";
+ print $file "<programlisting>\n";
+ print $file make_robodoc_rc_file( $spec );
+ print $file "</programlisting>\n";
+ print $file "</example>\n";
+
+ print $file "</section>\n";
+ }
+ }
+
+ $file->close();
+}
+
+
+
+make_examples_for_manual;
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ========== INSTALLING ===========
+ -->
+<section id="installing">
+ <title id="installing.title">Installing ROBODoc</title>
+ <para>The easiest way to install ROBODoc is to use one of the
+ packages. There are package for RedHat, Debian, OSX, and a precompiled
+ executable for Windows.</para>
+ <para>You can also compile --- from the sources. On a system
+ with <command>autoconfig</command> it is as simple as:</para>
+ <programlisting>
+./configure
+make
+make install
+</programlisting>
+ <para>This currently does not work with cygwin.</para>
+ <para>Under Windows you can use one of the makefile. There is a makefile for Borland C, MINGW,
+ and Cygwin. For other compilers you might want to try <filename>makefile.plain</filename>.
+</para>
+ <para>For instance for cygwin goto <filename>Source</filename> and run:</para>
+ <programlisting>
+make -f makefile.mingw-cygwin
+</programlisting>
+ <para>To install <command>ROBODoc</command> put the generated executable somewhere in your path.</para>
+ <para>You can test your executable, by going to the
+ <filename>Examples/PerlExample</filename> directory in the
+ archive, and running <command>robodoc</command>.
+ This should create a directory
+ called <filename>Doc</filename>. In there you should now
+ find a file called <filename>masterindex.html</filename>.
+</para>
+</section>
--- /dev/null
+body
+ {
+ margin-top:1em;
+ margin-bottom:1em;
+ margin-left:2.5em;
+ margin-right:2.5em;
+ font-family:sans-serif;
+ }
+
+/*
+a:link
+ {
+ color:#00FF00;
+ }
+
+a:visited
+ {
+ color:#003333;
+ }
+
+a:active
+ {
+ color:#FF00FF;
+ }
+*/
+
+p, form
+ {
+ font-family:sans-serif;
+ font-size:12pt;
+ }
+
+/*
+p
+ {
+ text-align:justify;
+ }
+*/
+
+b, strong, i, em, cite, var, tt, code, kbd, samp, img
+ {
+ display:inline;
+ }
+
+ul
+ {
+ margin-top:1em;
+ margin-bottom:1em;
+ list-style-type:disc;
+ display:block;
+ }
+
+li
+ {
+ margin-bottom:0.2em;
+ }
+
+b, strong
+ {
+ font-variant:small-caps;
+ font-weight:bold;
+ }
+
+i, cite, em, var, address, blockquote
+ {
+ font-style:italic;
+ }
+
+pre, tt, kbd, samp
+ {
+ font-family:monospace;
+ }
+
+
+table
+ {
+ color:#000000;
+ background-color:#AAAAAA;
+ }
+
+tt
+ {
+ white-space:pre;
+ }
+
+code
+ {
+ font-family:monospace;
+ font-style:normal;
+ white-space:pre;
+ }
+
+pre
+ {
+ white-space:pre;
+ margin-top:0.5em;
+ }
+
+address
+ {
+ font-family:monospace;
+ font-size:12pt;
+ text-align:left;
+ margin-bottom:0.5em;
+ }
+
+
+
+h2, h3
+ {
+ margin-top:0.5em;
+ }
+
+h1, h2, h3
+ {
+ font-weight:bold;
+ }
+
+h1
+ {
+ font-family:sans-serif;
+ font-size:24pt;
+ text-align:right;
+ margin-right:36px;
+ margin-top:0.5em;
+ margin-bottom:0.5em;
+ }
+
+h2
+ {
+ font-family:sans-serif;
+ font-size:18pt;
+ }
+
+h3
+ {
+ font-family:sans-serif;
+ font-size:14pt;
+ }
--- /dev/null
+## Process this file with automake to produce Makefile.in
+
+man_MANS = robodoc.1 robohdrs.1
+
+if FINK
+man1_MANS = robodoc.1 robohdrs.1
+else
+man1_MANS = robodoc.1 robohdrs.1
+endif
+
+#if FINK
+#docdir = $(prefix)/share/doc/$(PACKAGE)
+#else
+#docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+#endif
+
+if FINK
+doc_DATA = manual.html manual.css robodoc.rc
+else
+doc_DATA = manual.html manual.css robodoc.rc
+endif
+
+#
+# End of automake
+#
--- /dev/null
+# Make the documentation under the CYGWIN environment...
+# $Id: makefile.cygwin,v 1.3 2006/08/06 20:46:21 thuffir Exp $
+
+CP = cp
+RM = rm -rf
+
+SOURCE = manual.xml preparing.xml options.xml tips.xml installing.xml examples.xml extracting.xml bugs.xml configuration.xml header_examples.xml
+
+
+# Manual as a single HTML file
+manual.html : $(SOURCE) manual-cygwin.xsl
+ xsltproc manual-cygwin.xsl manual.xml > manual.html
+
+header_examples.xml : header_specs.pl
+ perl header_specs.pl
+
+# Manual in Windows help format.
+htmlhelp.chm : $(SOURCE) manual_html_help-cygwin.xsl
+ xsltproc manual_html_help-cygwin.xsl manual.xml
+ -hhc htmlhelp.hhp
+ $(CP) htmlhelp.chm manual.chm
+
+# Clean up.
+clean :
+ $(RM) \
+ manual.html \
+ index.html \
+ ar*.html \
+ *.chm \
+ *.hhc \
+ *.hhp \
+ header_examples.xml \
+
+all : manual.html htmlhelp.chm
+
+# Lint the manual to detect errors against the DTD.
+test : manual.html
+ xmllint --noent --noout manual.xml
--- /dev/null
+# $Id: makefile.plain,v 1.1 2007/06/23 16:29:59 gumpu Exp $
+
+SOURCE = manual.xml preparing.xml options.xml tips.xml installing.xml examples.xml extracting.xml bugs.xml configuration.xml header_examples.xml
+
+# Manual as a single HTML file
+manual.html : $(SOURCE) manual.xsl
+ xsltproc manual.xsl manual.xml > manual.html
+
+header_examples.xml : header_specs.pl
+ perl header_specs.pl
+
+
+clean :
+
+all : manual.html
+
+# Lint the manual to detect errors against the DTD.
+test :
+ xmllint --noent --noout manual.xml
--- /dev/null
+# $Id: makefile.win32,v 1.5 2007/02/06 22:31:38 gumpu Exp $
+
+SOURCE = manual.xml preparing.xml options.xml tips.xml installing.xml examples.xml extracting.xml bugs.xml configuration.xml header_examples.xml
+
+
+# Manual as a single HTML file
+manual.html : $(SOURCE) manual.xsl
+ xsltproc manual.xsl manual.xml > manual.html
+
+header_examples.xml : header_specs.pl
+ perl header_specs.pl
+
+# Manual in Windows help format.
+htmlhelp.chm : $(SOURCE) manual_html_help.xsl
+ xsltproc manual_html_help.xsl manual.xml
+ -hhc htmlhelp.hhp
+ copy htmlhelp.chm manual.chm
+
+clean :
+ -del ar*.html
+ -del *.chm
+ -del *.hhc
+ -del *.hhp
+ -del header_examples.xml
+
+all : manual.html htmlhelp.chm
+
+# Lint the manual to detect errors against the DTD.
+test :
+ xmllint --noent --noout manual.xml
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="/usr/share/docbook-xsl/html/docbook.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+body
+{
+ background-color: #ffffff;
+ color: #000000;
+ font-family: 'Lucida Grande', Verdana,
+ Geneva, Lucida, Arial,
+ Helvetica, sans-serif;
+ font-size: 10pt;
+ margin: 2% 5%;
+}
+h1.title
+{
+ color: #000000;
+ text-align: left;
+ font-size: 16pt;
+ margin-top: 10%;
+}
+
+h2.title
+{
+ color: #000000;
+ text-align: left;
+ font-size: 14pt;
+ margin-top: 3%;
+}
+h3.title
+{
+ color: #000000;
+ text-align: left;
+ font-size: 11pt;
+ margin-top: 3%;
+}
+
+h3.author
+{
+ color: #000000;
+ text-align: left;
+ font-size: 10pt;
+}
+td.even, td.uneven
+{
+ color: #000000;
+ font-size: 10pt;
+}
+td.even
+{
+ background-color: #eeeeee;
+}
+pre.programlisting, pre.literallayout
+{
+ background:#ffe;
+ font-family: monospace;
+ border:solid #aa9 2px;
+ margin:4px;
+ white-space: pre;
+}
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>ROBODoc 4.99.34 User Manual</title><link rel="stylesheet" href="manual.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.72.0" /></head><body><div class="article" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 class="title"><a id="id2434377"></a>ROBODoc 4.99.34 User Manual</h1></div><div><div class="authorgroup"><div class="author"><h3 class="author"><span class="firstname">Frans</span> <span class="surname">Slothouber</span></h3></div><div class="author"><h3 class="author"><span class="firstname">Petteri</span> <span class="surname">Kettunen</span></h3></div><div class="author"><h3 class="author"><span class="firstname">Gergely</span> <span class="surname">Budai</span></h3></div></div></div><div><p class="copyright">Copyright © 1994-2007
+ Frans Slothouber, Petteri Kettunen,
+ Jacco van Weert, Gergely Budai
+ </p></div><div><p class="pubdate">Apr 2007</p></div></div><hr /></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id2488392">1. Preface</a></span></dt><dt><span class="section"><a href="#installing">2. Installing ROBODoc</a></span></dt><dt><span class="section"><a href="#preparing">3. Preparing your source code for ROBODoc</a></span></dt><dd><dl><dt><span class="section"><a href="#id2495696">3.1. Headers</a></span></dt><dt><span class="section"><a href="#header_types">3.2. Header Types</a></span></dt><dt><span class="section"><a href="#id2496633">3.3. Items</a></span></dt><dt><span class="section"><a href="#sections">3.4. Sections</a></span></dt><dt><span class="section"><a href="#formatting">3.5. Smart Text Formatting</a></span></dt></dl></dd><dt><span class="section"><a href="#id2498762">4. Extracting Documentation with ROBODoc</a></span></dt><dd><dl><dt><span class="section"><a href="#id2498774">4.1. Single document or many smaller documents</a></span></dt><dt><span class="section"><a href="#id2497592">4.2. multidoc</a></span></dt><dt><span class="section"><a href="#id2498822">4.3. singledoc</a></span></dt><dt><span class="section"><a href="#id2498869">4.4. singlefile</a></span></dt><dt><span class="section"><a href="#id2498898">4.5. Output formats</a></span></dt></dl></dd><dt><span class="section"><a href="#id2498342">5. Examples of How to Create Documentation</a></span></dt><dd><dl><dt><span class="section"><a href="#id2498347">5.1. HTML Example</a></span></dt><dt><span class="section"><a href="#id2498374">5.2. RTF Example</a></span></dt><dt><span class="section"><a href="#id2498448">5.3. LaTeX Example</a></span></dt><dt><span class="section"><a href="#id2498537">5.4. XML DocBook Example</a></span></dt></dl></dd><dt><span class="section"><a href="#options">6. Options</a></span></dt><dt><span class="section"><a href="#customizing">7. Customizing ROBODoc</a></span></dt><dd><dl><dt><span class="section"><a href="#id2502679">7.1. items block</a></span></dt><dt><span class="section"><a href="#id2502700">7.2. ignore items block</a></span></dt><dt><span class="section"><a href="#id2502714">7.3. item order block</a></span></dt><dt><span class="section"><a href="#sourceitems">7.4. source items block</a></span></dt><dt><span class="section"><a href="#id2502749">7.5. preformatted items block</a></span></dt><dt><span class="section"><a href="#id2502783">7.6. format items block</a></span></dt><dt><span class="section"><a href="#id2502813">7.7. options block</a></span></dt><dt><span class="section"><a href="#headertypes_block">7.8. headertypes block</a></span></dt><dt><span class="section"><a href="#id2502896">7.9. ignore files block</a></span></dt><dt><span class="section"><a href="#id2502963">7.10. accept files block</a></span></dt><dt><span class="section"><a href="#id2502981">7.11. header markers block</a></span></dt><dt><span class="section"><a href="#id2502994">7.12. remark markers block</a></span></dt><dt><span class="section"><a href="#id2503006">7.13. end markers block</a></span></dt><dt><span class="section"><a href="#separate_characters_block">7.14. header separate characters block</a></span></dt><dt><span class="section"><a href="#header_ignore_characters_block">7.15. header ignore characters block</a></span></dt><dt><span class="section"><a href="#remark_begin_end">7.16.
+remark begin markers and remark end markers block</a></span></dt><dt><span class="section"><a href="#linecomments">7.17. source line comments block</a></span></dt><dt><span class="section"><a href="#keywords">7.18. keywords block</a></span></dt><dt><span class="section"><a href="#id2503510">7.19. Configuration file location</a></span></dt></dl></dd><dt><span class="section"><a href="#id2504528">8. Tips and Tricks</a></span></dt><dd><dl><dt><span class="section"><a href="#id2504533">8.1. The SOURCE Item</a></span></dt><dt><span class="section"><a href="#id2504567">8.2. Minimizing Duplicate Information</a></span></dt><dt><span class="section"><a href="#id2504666">8.3. Advanced formatting with raw HTML and LaTeX code</a></span></dt><dt><span class="section"><a href="#id2504752">8.4. Linking to external documents (href, file, mailto, images)</a></span></dt><dt><span class="section"><a href="#id2504846">8.5. Linking from an external document</a></span></dt><dt><span class="section"><a href="#tools">8.6. Using external tools</a></span></dt><dt><span class="section"><a href="#id2505111">8.7. ROBODoc-ing an existing project</a></span></dt><dt><span class="section"><a href="#id2505169">8.8. Using ROBODoc under Windows</a></span></dt></dl></dd><dt><span class="section"><a href="#defaultheaders">9. Languages Supported by Default</a></span></dt><dd><dl><dt><span class="section"><a href="#id2504474">9.1. C</a></span></dt><dt><span class="section"><a href="#id2504152">9.2. Modula-2</a></span></dt><dt><span class="section"><a href="#id2506135">9.3. Pascal</a></span></dt><dt><span class="section"><a href="#id2506189">9.4. C++</a></span></dt><dt><span class="section"><a href="#id2506241">9.5. Tcl</a></span></dt><dt><span class="section"><a href="#id2506293">9.6. Perl</a></span></dt><dt><span class="section"><a href="#id2506344">9.7. LaTeX/TeX</a></span></dt><dt><span class="section"><a href="#id2506397">9.8. Postscript</a></span></dt><dt><span class="section"><a href="#id2506449">9.9. Occam</a></span></dt></dl></dd><dt><span class="section"><a href="#id2505449">10. Suggestions and Bugs</a></span></dt></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2488392"></a>1. Preface</h2></div></div></div><p>ROBODoc is an API documentation tool for C, C++, Java, Assembler,
+ Basic, Fortran, LaTeX, Postscript, Tcl/Tk, LISP, Forth, Perl, Shell
+ Scripts, Makefiles, Occam, COBOL, DCL, Visual Basic, HTML, DB/C, XML,
+ and many other languages. It can be made to work with any language that
+ supports comments.</p><p>ROBODoc works by extracting specially formatted headers from your
+ source code and writes these to documentation files. These files can be
+ formatted in HTML, ASCII, XML DocBook, or RTF; and indirectly to
+ PDF and HTML Help.</p><p>ROBODoc is similar to JavaDoc, though the idea is much
+ older than JavaDoc. ROBODoc allows you to maintain a program and
+ its documentation in a single file. This makes it easier to keep
+ your documentation up-to-date.</p><p>ROBODoc can be used to document anything you like,
+ functions, methods, variables, definitions, test cases, makefile
+ entries, and anything else you can think of.</p><p>It can create documentation consisting of many small files.
+ For instance in HTML format for easy browsing and publication on
+ the web. It can also create a single file in LaTeX or RTF format
+ for inclusion into bigger design documents. The RTF format is
+ suited to be included in Word documents.</p><p>ROBODoc allows you to separate internal documentation from
+ external documentation. In singledoc mode it can create a section
+ layout based on the hierarchy of your modules.</p><p>ROBODoc is designed to work with a lot of different
+ programming languages. It has no knowledge of the syntax of
+ programming languages. It only has some knowledge about how
+ comments start and end in a lot of programming languages. This
+ means that you sometimes have to do a little more work compared to
+ other tools that have detailed knowledge of the syntax of a
+ particular language. They can use that knowledge to figure out
+ some of the information automatically. This usually also means
+ that they work only with one or two languages. </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="installing"></a>2. Installing ROBODoc</h2></div></div></div><p>The easiest way to install ROBODoc is to use one of the
+ packages. There are package for RedHat, Debian, OSX, and a precompiled
+ executable for Windows.</p><p>You can also compile --- from the sources. On a system
+ with <span><strong class="command">autoconfig</strong></span> it is as simple as:</p><pre class="programlisting">
+./configure
+make
+make install
+</pre><p>This currently does not work with cygwin.</p><p>Under Windows you can use one of the makefile. There is a makefile for Borland C, MINGW,
+ and Cygwin. For other compilers you might want to try <code class="filename">makefile.plain</code>.
+</p><p>For instance for cygwin goto <code class="filename">Source</code> and run:</p><pre class="programlisting">
+make -f makefile.mingw-cygwin
+</pre><p>To install <span><strong class="command">ROBODoc</strong></span> put the generated executable somewhere in your path.</p><p>You can test your executable, by going to the
+ <code class="filename">Examples/PerlExample</code> directory in the
+ archive, and running <span><strong class="command">robodoc</strong></span>.
+ This should create a directory
+ called <code class="filename">Doc</code>. In there you should now
+ find a file called <code class="filename">masterindex.html</code>.
+</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="preparing"></a>3. Preparing your source code for ROBODoc</h2></div></div></div><p> ROBODoc allows you to mix the program documentation with
+ the source code. It does require though that this documentation
+ has a particular layout so ROBODoc can recognize it. There are
+ three key concepts: headers, items, and sections. </p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2495696"></a>3.1. Headers</h3></div></div></div><p>Headers are the building blocks of the documentation. Lets
+ look at an example. The following header was taken from the
+ documentation of the predecessor of ROBODoc, AutoDoc.</p><div class="example"><a id="robodocheader"></a><p class="title"><b>Example 1. A ROBODoc header in C.</b></p><div class="example-contents"><pre class="programlisting">
+ /****f* financial.library/StealMoney
+ * NAME
+ * StealMoney -- Steal money from the Federal Reserve Bank. (V77)
+ * SYNOPSIS
+ * error = StealMoney( userName, amount, destAccount, falseTrail )
+ * FUNCTION
+ * Transfer money from the Federal Reserve Bank into the
+ * specified interest-earning checking account. No records of
+ * the transaction will be retained.
+ * INPUTS
+ * userName - name to make the transaction under. Popular
+ * favorites include "Ronald Reagan" and
+ * "Mohamar Quadaffi".
+ * amount - Number of dollars to transfer (in thousands).
+ * destAccount - A filled-in AccountSpec structure detailing the
+ * destination account (see financial/accounts.h).
+ * If NULL, a second Great Depression will be
+ * triggered.
+ * falseTrail - If the DA_FALSETRAIL bit is set in the
+ * destAccount, a falseTrail structure must be
+ * provided.
+ * RESULT
+ * error - zero for success, else an error code is returned
+ * (see financial/errors.h).
+ * EXAMPLE
+ * Federal regulations prohibit a demonstration of this function.
+ * NOTES
+ * Do not run on Tuesdays!
+ * BUGS
+ * Before V88, this function would occasionally print the
+ * address and home phone number of the caller on local police
+ * 976 terminals. We are confident that this problem has been
+ * resolved.
+ * SEE ALSO
+ * CreateAccountSpec(),security.device/SCMD_DESTROY_EVIDENCE,
+ * financial/misc.h
+ ******
+ * You can use this space for remarks that should not be included
+ * in the documentation.
+ */
+</pre></div></div><br class="example-break" /><p>A header consists of three different elements: a
+ begin marker, a number of items, and an end marker. The begin marker
+ in the example is:</p><pre class="programlisting">
+ ****f* financial.library/StealMoney
+</pre><p>It marks the beginning of a header. It also tells ROBODoc
+</p><div class="itemizedlist"><ul type="disc"><li><p>the name of the element that is being documented, StealMoney,</p></li><li><p>the module it is part of, financial.library,</p></li><li><p>the kind of element, <code class="literal">f</code>, which stands for function.</p></li></ul></div><p>
+ ROBODoc always expects that a <code class="literal">/</code> separates the module name and an element name.
+ So <code class="literal">ModFoo/funcBar</code>
+ is a valid name, but <code class="literal">funcBar</code> is not.
+ See <a href="#sections" title="3.4. Sections">Sections</a> for more
+ information.
+</p><p>
+ Names can also contain spaces but ROBODoc won't create links to names with
+ embedded spaces.
+</p><p>
+ You can also have multiple names for a header. This is useful if you
+ document similar objects together in one header (for example assembly
+ subroutines with multiple jump-in points). Multiple names are separated by
+ commas and can span over more than one line.
+</p><pre class="programlisting">
+ ****f* financial.library/StealMoney, Steal_Money
+</pre><p>
+ In the above example all references found to <code class="literal">StealMoney</code>
+ and <code class="literal">Steal_Money</code> in other headers will be automatically
+ linked to this header.
+ The separation character(s) can be specified by the
+ <a href="#separate_characters_block" title="7.14. header separate characters block">header separate characters block</a>.
+ See <a href="#customizing" title="7. Customizing ROBODoc">Customizing ROBODoc</a> for more
+ information.
+</p><p>
+The end marker:
+</p><pre class="programlisting">
+ ******
+</pre><p>
+ marks the end of a header.
+ </p><p>Items begin with an item name and are followed by the
+ item's body. An example: </p><pre class="programlisting">
+ * FUNCTION
+ * Transfer money from the Federal Reserve Bank into the
+ * specified interest-earning checking account. No records of
+ * the transaction will be retained.
+</pre><p>
+ In this case the item's name is FUNCTION.
+ </p><p>
+ Each line of an item starts with a remark marker. In this case
+ <code class="literal">*</code>.
+ </p><p>That what ROBODoc needs to recognize a header is therefore:</p><div class="example"><a id="id2488316"></a><p class="title"><b>Example 2. The markers needed by ROBODoc to recognize a header.</b></p><div class="example-contents"><pre class="programlisting">
+ /****f* financial.library/StealMoney
+ * NAME
+ *
+ * SYNOPSIS
+ *
+ * FUNCTION
+ *
+ *
+ *
+ * INPUTS
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * RESULT
+ *
+ *
+ * EXAMPLE
+ *
+ * NOTES
+ *
+ * BUGS
+ *
+ *
+ *
+ *
+ * SEE ALSO
+ *
+ *
+ ******
+</pre></div></div><br class="example-break" /><p>
+ The above example is in C. ROBODoc supports many more
+ languages though. See <a href="#defaultheaders" title="9. Languages Supported by Default">Languages Supported by Default</a>.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="header_types"></a>3.2. Header Types</h3></div></div></div><p> ROBODoc defines a number of header types. You don't need
+ to use them but they can be useful for sorting information. The
+ header type tells ROBODoc what kind of object you are documenting.
+ This information allows ROBODoc to create more useful index
+ tables.</p><p>The type is identified by one or two characters. ROBODoc
+ expects to find them after the fourth <code class="literal">*</code> in the
+ begin marker. So <code class="literal">#****f</code> is a valid marker,
+ but <code class="literal">#**f**</code> is not.</p><p>If a single character is given, the type is defined as
+ listed in the following table</p><div class="table"><a id="id2496438"></a><p class="title"><b>Table 1. Default header types</b></p><div class="table-contents"><table summary="Default header types" border="1"><colgroup><col align="left" /><col align="left" /></colgroup><tbody><tr><td align="left">c</td><td align="left">Header for a class </td></tr><tr><td align="left">d</td><td align="left">Header for a constant (from define)</td></tr><tr><td align="left">f</td><td align="left">Header for a function</td></tr><tr><td align="left">h</td><td align="left"><a id="header_type_h"></a>Header for a module in a project</td></tr><tr><td align="left">m</td><td align="left">Header for a method</td></tr><tr><td align="left">s</td><td align="left">Header for a structure</td></tr><tr><td align="left">t</td><td align="left">Header for a types</td></tr><tr><td align="left">u</td><td align="left">Header for a unit test</td></tr><tr><td align="left">v</td><td align="left">Header for a variable</td></tr><tr><td align="left">*</td><td align="left">Generic header for everything else</td></tr></tbody></table></div></div><br class="table-break" /><p>If two characters are given, the first character should be
+ <code class="literal">i</code> and the second can be any of the other
+ characters from the table above. This creates an internal header
+ of the type specified by the second character. Internal headers
+ are special. They can be used to hide certain headers. They are
+ only extracted if requested. You can use them to document internal
+ functions, classes, etc. that you do not want clients to see,
+ creating what might be a programmer's manual as opposed to a
+ user's manual.</p><p>So <code class="literal">/****if* Module/func1</code> defines an
+ internal function called <code class="literal">func1</code>.
+ </p><p>Headers marked internal are by default not included in the
+ generated documentation. If you want to include them use the
+ option <code class="option">--internal</code>. You can also generate the
+ documentation from only the internal headers with the option
+ <code class="option">--internalonly</code>.
+ </p><p>You can define your own header types using the ROBODoc
+ configuration file, <code class="filename">robodoc.rc</code>.
+ See <a href="#headertypes_block" title="7.8. headertypes block">headertypes block</a>.
+ This way you can document anything you like, for instance makefile
+ entries, system tests, or exceptions.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2496633"></a>3.3. Items</h3></div></div></div><p> By default ROBODoc recognizes the following items: </p><div class="variablelist"><dl><dt><span class="term">
+ NAME
+ </span></dt><dd><p>Item name plus a short description.
+ </p></dd><dt><span class="term">
+ COPYRIGHT
+ </span></dt><dd><p>Who own the copyright : "(c) <year>-<year> by
+ <company/person>"
+ </p></dd><dt><span class="term">
+ SYNOPSIS
+ , </span><span class="term">
+ USAGE
+ </span></dt><dd><p>
+ How to use it.
+ </p></dd><dt><span class="term">
+ FUNCTION
+ , </span><span class="term">
+ DESCRIPTION
+ , </span><span class="term">
+ PURPOSE
+ </span></dt><dd><p>
+ What does it do.
+ </p></dd><dt><span class="term">
+ AUTHOR
+ </span></dt><dd><p>
+ Who wrote it.
+ </p></dd><dt><span class="term">
+ CREATION DATE
+ </span></dt><dd><p>When did the work start.
+ </p></dd><dt><span class="term">
+ MODIFICATION HISTORY
+ , </span><span class="term">
+ HISTORY
+ </span></dt><dd><p>Who has done which changes and when.
+ </p></dd><dt><span class="term">
+ INPUTS
+ , </span><span class="term">
+ ARGUMENTS
+ , </span><span class="term">
+ OPTIONS
+ , </span><span class="term">
+ PARAMETERS
+ , </span><span class="term">
+ SWITCHES
+ </span></dt><dd><p>
+ What can we feed into it.
+ </p></dd><dt><span class="term">
+ OUTPUT
+ , </span><span class="term">
+ SIDE EFFECTS
+ </span></dt><dd><p>What output is made.
+ </p></dd><dt><span class="term">
+ RESULT
+ , </span><span class="term">
+ RETURN VALUE
+ </span></dt><dd><p>
+ What do we get returned.
+ </p></dd><dt><span class="term">
+ EXAMPLE
+ </span></dt><dd><p>
+ A clear example of the items use.
+ </p></dd><dt><span class="term">
+ NOTES
+ </span></dt><dd><p>
+ Any annotations
+ </p></dd><dt><span class="term">
+ DIAGNOSTICS
+ </span></dt><dd><p> Diagnostic output.
+ </p></dd><dt><span class="term">
+ WARNINGS
+ , </span><span class="term">
+ ERRORS
+ </span></dt><dd><p>Warning and error-messages.
+ </p></dd><dt><span class="term">
+ BUGS
+ </span></dt><dd><p>Known bugs.
+ </p></dd><dt><span class="term">
+ TODO
+ , </span><span class="term">
+ IDEAS
+ </span></dt><dd><p>What to implement next and ideas. </p></dd><dt><span class="term">
+ PORTABILITY
+ </span></dt><dd><p>Where does it come from, where will it work. </p></dd><dt><span class="term">
+ SEE ALSO
+ </span></dt><dd><p>References to other functions, man pages, other documentation. </p></dd><dt><span class="term">
+ METHODS
+ , </span><span class="term">
+ NEW METHODS
+ </span></dt><dd><p>OOP methods.</p></dd><dt><span class="term">
+ ATTRIBUTES
+ , </span><span class="term">
+ NEW ATTRIBUTES
+ </span></dt><dd><p>OOP attributes.</p></dd><dt><span class="term">
+ TAGS
+ </span></dt><dd><p>Tag-item description.
+ </p></dd><dt><span class="term">
+ DERIVED FROM
+ </span></dt><dd><p>OOP super class. </p></dd><dt><span class="term">
+ DERIVED BY
+ </span></dt><dd><p>OOP sub class. </p></dd><dt><span class="term">
+ USES
+ , </span><span class="term">
+ CHILDREN
+ </span></dt><dd><p>What modules are used by this one. </p></dd><dt><span class="term">
+ USED BY
+ , </span><span class="term">
+ PARENTS
+ </span></dt><dd><p>Which modules do use this one. </p></dd><dt><span class="term">
+ COMMANDS
+ </span></dt><dd><p>Command description. </p></dd><dt><span class="term">
+ SOURCE
+ </span></dt><dd><p>Source code inclusion. </p></dd></dl></div><p>You can define your own items using the ROBODoc
+ configuration file, <code class="filename">robodoc.rc</code>. See <a href="#customizing" title="7. Customizing ROBODoc">Customizing ROBODoc</a>. </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sections"></a>3.4. Sections</h3></div></div></div><p>The structure of source code for a project is usually
+ hierarchical. A project might consists of several applications,
+ an application of several modules, a module of several functions
+ or even sub modules. ROBODoc allows you to show this hierarchy in
+ your documentation. For this you specify the hierarchy in the
+ header name. For instance, you have a project that is going to
+ create a new language called D. The D Language project might
+ consists of three applications: a preprocessor, a compiler, and a
+ linker. The compiler consists of two modules, a parser and a
+ generator. The parser module consists of several
+ functions.</p><p>The following three headers show how this hierarchy can be
+ defined in the header name.</p><pre class="programlisting">
+#****h* D-Language/Compiler
+# FUNCTION
+# The compiler takes a preprocessed source file and
+# turns it into an object file.
+#***
+</pre><pre class="programlisting">
+#****h* D-Language/Linker
+# FUNCTION
+# The linker module contains functions that scan a
+# object file and build the executable.
+#***
+</pre><pre class="programlisting">
+#****h* Compiler/Parser
+# FUNCTION
+# The parser module contains functions that scan a
+# preprocessed source file and build the syntax tree.
+#***
+</pre><pre class="programlisting">
+#****f* Parser/ReadToken
+# FUNCTION
+# ReadToken reads the next token from the input
+# file.
+#***
+</pre><p>When you generate documentation with the option
+ <code class="option">--section</code>, ROBODoc uses the hierarchical
+ information when generating the table of contents and document
+ section information. For instance in HTML sections are started
+ with <H1>, <H2>, <H3> depending on the level
+ in the hierarchy. The table of contents will also contain levels. The
+ table of contents for the above example will be: </p><pre class="programlisting">
+1. D-Language/Compiler
+1.1 Compiler/Parser
+1.1.1 Parser/ReadToken
+2. D-Language/Linker
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="formatting"></a>3.5. Smart Text Formatting</h3></div></div></div><p>By default ROBODoc creates preformatted text in the output
+documentation for all the text it finds in an item. This means
+that the formatting of the output looks the same as the formatting of
+the text of an item. Line-breaks and indentation stay the same.
+This is easy but does not always create the best looking
+output.</p><p>ROBODoc can also try to deduce the formatting of your text based
+on the layout and indentation of your text and on special characters in the text.
+It works a bit similar to the input method of Wikis. In the context of this
+manual this is called Smart Formatting.
+</p><p>You switch this on with the option <code class="option">--nopre</code>.
+ROBODoc now tries to find three kind of elements: paragraphs,
+lists, and preformatted text.</p><p>Paragraphs are separated by empty lines. So the following item
+has two paragraphs.</p><div class="example"><a id="id2497225"></a><p class="title"><b>Example 3. Two paragraphs.</b></p><div class="example-contents"><pre class="programlisting">
+ FUNCTION
+ This function does something.
+
+ And it then does something else
+ and a bit more.
+</pre></div></div><br class="example-break" /><p> A List starts with a line that ends with a ':' which is then
+followed by one or more list items. List items should start with '*',
+'-', or 'o'. So the following item contains a valid list: </p><div class="example"><a id="id2497246"></a><p class="title"><b>Example 4. A list.</b></p><div class="example-contents"><pre class="programlisting">
+ FUNCTION
+ This function does:
+ * a lot of foo
+ * some snafuing
+ * and a bit of foobarring.
+</pre></div></div><br class="example-break" /><p> A list item can span more than one line if the second and following
+lines are indented. So this is also a valid list:</p><div class="example"><a id="id2497265"></a><p class="title"><b>Example 5. A list where one of the items spans more than one line.</b></p><div class="example-contents"><pre class="programlisting">
+ FUNCTION
+ This function does:
+ * a lot of foo and preprocessing of the
+ raw input with the aid
+ of the some magic
+ * some snafuing
+ * and a bit of foobarring.
+</pre></div></div><br class="example-break" /><p>If list items directly follow the name of a robodoc item they
+also form a valid list. So this is a valid list:</p><div class="example"><a id="id2497287"></a><p class="title"><b>Example 6. an implicit list</b></p><div class="example-contents"><pre class="programlisting">
+ INPUTS
+ * inputname -- the name of the input file
+ * outputname -- the name of the output file
+ * n -- the number of characters to be written
+</pre></div></div><br class="example-break" /><p> Preformatted text is indicated by indenting it more that the
+surrounding text. The first non-empty line in an item determines the
+base indenting. Any lines with an indentation larger than this are
+preformatted. An example:</p><div class="example"><a id="id2497309"></a><p class="title"><b>Example 7. Preformatted text</b></p><div class="example-contents"><pre class="literallayout">
+ FUNCTION
+ The following lines are preformatted
+ +--------+
+ | A box |
+ | |
+ +--------+
+ End of the preformatted text.
+</pre></div></div><br class="example-break" /><p>The following is a complete example.</p><div class="example"><a id="fullsmart"></a><p class="title"><b>Example 8. All three elements of smart formatting.</b></p><div class="example-contents"><pre class="literallayout">
+ This is some example text.
+ And some more.
+
+ This is even more, and we start a list:
+ * a list item
+ * a list item
+ * a list item
+
+ And we can also do preformatted stuff
+ by indenting
+ +--------+
+ | |
+ +--------+
+ The box will stay.
+</pre></div></div><br class="example-break" /><p>will be turn into</p><div class="example"><a id="fulloutput"></a><p class="title"><b>Example 9. The corresponding HTML output.</b></p><div class="example-contents"><pre class="literallayout">
+ <p>This is some example text.
+ And some more.</p>
+
+ <p>This is even more, and we start a list:</p>
+ <ul>
+ <li>a list item</li>
+ <li>a list item</li>
+ <li>a list item</li>
+ </ul>
+
+ <p>And we can also do preformatted stuff
+ by indenting</p>
+ <pre>
+ +--------+
+ | |
+ +--------+
+ </pre>
+ <p> The box will stay.</p>
+</pre></div></div><br class="example-break" /></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2498762"></a>4. Extracting Documentation with ROBODoc</h2></div></div></div><p>Now that you have prepared your source code for use with
+ ROBODoc you are ready to extract the documentation. There are
+ several choices to be made.</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498774"></a>4.1. Single document or many smaller documents</h3></div></div></div><p>First of all, ROBODoc can be used in three modes.</p><div class="itemizedlist"><ul type="disc"><li><p>multidoc -- in this mode ROBODoc scans
+ all the source files in your source directory and creates a
+ separate document file for each of these in a document directory.
+ The document directory is created automatically. Its structure is
+ a mirror of the structure of your source directory.</p></li><li><p>singledoc -- in this mode ROBODoc scans all the source
+ files in your source directory and creates a single documentation
+ file that contains all the documentation extracted from your
+ source files. </p></li><li><p>singlefile -- in this mode ROBODoc scans a single source
+ file and creates a single documentation file.</p></li></ul></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2497592"></a>4.2. multidoc</h3></div></div></div><p>The multidoc mode is useful to create browsable documents.
+ For instance many small HTML files that can be viewed with a
+ web-browser. This mode requires the following arguments:</p><p>
+ <span><strong class="command">robodoc
+ --src <em class="replaceable"><code>source directory</code></em>
+ --doc <em class="replaceable"><code>document directory</code></em>
+ --multidoc
+ other options
+ </strong></span>
+</p><p>An additional option that is useful with this mode is
+ <code class="option">--index</code>, this creates a series of index files,
+ one for each header type.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498822"></a>4.3. singledoc</h3></div></div></div><p> The singledoc mode is useful to create bulk documentation
+ that can be incorporated in other documents, or that can be
+ delivered to a client as a single document. For instance a file
+ created in RTF format can be included into a larger design
+ document written in Word format. This mode requires the following
+ arguments:</p><p>
+ <span><strong class="command">robodoc
+ --src <em class="replaceable"><code>source directory</code></em>
+ --doc <em class="replaceable"><code>document file without extension</code></em>
+ --singledoc
+ <em class="replaceable"><code>other options</code></em>
+ </strong></span>
+</p><p>An additional option that is useful with this mode is
+ <code class="option">--sections</code>, this causes the headers to follow a
+ section layout based on the module element hierarchy defined in the
+ header name.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498869"></a>4.4. singlefile</h3></div></div></div><p>The singlefile mode is not very useful. It is mainly used
+ for debugging purposes. This mode requires the following
+ arguments:</p><p>
+ <span><strong class="command">robodoc
+ --src <em class="replaceable"><code>source file</code></em>
+ --doc <em class="replaceable"><code>document file</code></em>
+ --singlefile
+ other options
+</strong></span>
+</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498898"></a>4.5. Output formats</h3></div></div></div><p>Your next choice is the output format. ROBODoc can create
+ documentation in several formats:</p><div class="itemizedlist"><ul type="disc"><li><p>HTML, option <code class="option">--html</code></p></li><li><p>RTF, option <code class="option">--rtf</code></p></li><li><p>LaTeX, option <code class="option">--latex</code></p></li><li><p>XML DocBook, option <code class="option">--dbxml</code></p></li></ul></div><p>What format to use depends on your wishes. If you want a
+ single printable document, use LaTeX or XML DocBook. If you want
+ a document that can be included into a larger (Word) document use
+ RTF. If you want something that is browsable use HTML, or use XML
+ DocBook and then convert it to HTML.</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2498342"></a>5. Examples of How to Create Documentation</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498347"></a>5.1. HTML Example</h3></div></div></div><p> For this you need a web browser, say FireFox or Mozilla. You can try
+ this in the robodoc root directory. It creates a document called
+ <code class="filename">HDocs/masterindex.html</code> plus a lot of smaller
+ documents from all the source files in the directory
+ <code class="filename">Source</code>.</p><pre class="programlisting">
+<span><strong class="command">robodoc</strong></span> <code class="option">--src</code> ./Source <code class="option">--doc</code> ./HDocs <code class="option">--multidoc</code> <code class="option">--index</code> <code class="option">--html</code>
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498374"></a>5.2. RTF Example</h3></div></div></div><p>For this you need an rtf reader, for instance
+ <span><strong class="command">Word</strong></span>. You can try this in the robodoc root
+ directory.</p><pre class="programlisting">
+<span><strong class="command">robodoc</strong></span> <code class="option">--src</code> ./Source <code class="option">--doc</code> api <code class="option">--singledoc</code> <code class="option">--rtf</code> <code class="option">--sections</code>
+</pre><p>This will create a document called
+ <code class="filename">api.rtf</code>.</p><p>By default the document looks pretty plain. There is no
+ chapter numbering or a table of contents, even if you asked for
+ it. All the information for this is included but not visible.
+ This is because chapter numbering and a table of contents are
+ generated by Word based on formatting information that is part of
+ a Word document but not part of a RTF document. </p><p>To make it visible you include the generated document into a
+ bigger document with the right formatting options. This is best
+ done with a cut-and-paste operation. Use the cut-past-paste
+ special menu, and paste it as RTF formatted text into your Word
+ document.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498448"></a>5.3. LaTeX Example</h3></div></div></div><p> For this you need <span><strong class="command">latex</strong></span> and
+ <span><strong class="command">makeindex</strong></span>. You can try this in the robodoc root
+ directory. It creates a single document called
+ <code class="filename">api.dvi</code> from all the source files in the
+ directory Source.</p><pre class="programlisting">
+<span><strong class="command">robodoc</strong></span> <code class="option">--src</code> ./Source <code class="option">--doc</code> api <code class="option">--singledoc</code> <code class="option">--latex</code> <code class="option">--sections</code>
+<span><strong class="command">latex</strong></span> api.tex
+<span><strong class="command">latex</strong></span> api.tex
+<span><strong class="command">makeindex</strong></span> api.idx
+<span><strong class="command">latex</strong></span> api.tex
+<span><strong class="command">xdvi</strong></span> api.dvi
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2498537"></a>5.4. XML DocBook Example</h3></div></div></div><p>
+ DocBook is a xml format to create technical documentation, see.
+ <a href="http://www.docbook.org/" target="_top"><em class="citetitle">DocBook.org</em></a>.
+ DocBook is quite nice. This manual for instance is written in DocBook and
+ automatically translated into html and pdf format.
+</p><p>
+ You can use the DocBook output of ROBODoc to create several other formats,
+ for instance: html, pdf, html-help.
+ For this you need a tool that can process a DocBook file. There
+ are several of these tools.
+</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2498564"></a>5.4.1. DocBook with html output</h4></div></div></div><p>The easiest to use is <span><strong class="command">xsltproc</strong></span>. It works under
+ Windows and Unix. A typical workflow under Windows is:
+</p><pre class="programlisting">
+<span><strong class="command">robodoc</strong></span> <code class="option">--src</code> ./Source <code class="option">--doc</code> api <code class="option">--singledoc</code> <code class="option">--dbxml</code> <code class="option">--sections</code>
+<span><strong class="command">xsltproc</strong></span> api.xsl api.xml > api.html
+</pre><p>
+ Where <code class="filename">api.xsl</code> contains:
+</p><pre class="literallayout">
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/html/docbook.xsl"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
+</pre><p>For this you need <span><strong class="command">xsltproc</strong></span>. For Windows these can be found at
+ <a href="http://www.zlatkovic.com/libxml.en.html" target="_top">
+ <em class="citetitle">http://www.zlatkovic.com libxml.en.html</em></a>,
+ and the stylesheets which can be found at
+ <a href="http://docbook.sourceforge.net/" target="_top"><em class="citetitle">http://docbook.sourceforge.net/</em></a>.
+ In the example above the style sheets are installed on <code class="filename">e:/docbook/</code>.
+</p><p>
+ More information about xsl can be found at
+ <a href="http://www.sagehill.net/docbookxsl/" target="_top"><em class="citetitle">http://www.sagehill.net/docbookxsl/</em></a>.
+</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h4 class="title"><a id="id2499568"></a>5.4.2. DocBook with html help output</h4></div></div></div><p>The same program can be used to
+ <a href="http://docbook.sourceforge.net/release/xsl/current/doc/htmlhelp.html" target="_top">
+ <em class="citetitle">create a html help</em>
+ </a> file. For this you need
+ <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp" target="_top">
+ <em class="citetitle">HTML Help Workshop</em>
+ </a>. The workflow now is:
+</p><pre class="programlisting">
+<span><strong class="command">robodoc</strong></span> <code class="option">--src</code> ./Source <code class="option">--doc</code> api <code class="option">--singledoc</code> <code class="option">--dbxml</code> <code class="option">--sections</code>
+<span><strong class="command">xsltproc</strong></span> api.xsl api.xml
+<span><strong class="command">hhc</strong></span> htmlhelp.hhp
+</pre><p>
+ Where <code class="filename">api.xsl</code> contains:
+</p><pre class="literallayout">
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/htmlhelp/htmlhelp.xsl"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
+</pre></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="options"></a>6. Options</h2></div></div></div><p>The behavior of ROBODoc can be further fine-tune with a large number of
+ options.</p><div class="variablelist"><dl><dt><span class="term">-c</span></dt><dd><p>Show the copyright message.</p></dd><dt><a id="cmode"></a><span class="term"><a id="cmode.term"></a>--cmode</span></dt><dd><p>Use ANSI C grammar in SOURCE items and use this
+ for syntax highlighting (<code class="literal">HTML</code> only).</p></dd><dt><span class="term">--css</span></dt><dd><p> Use the content of the specified file to create the
+ <code class="filename">robodoc.css</code>. The content of the file is
+ copied into <code class="filename">robodoc.css</code>. </p></dd><dt><span class="term">--dbxml</span></dt><dd><p>Generate documentation in XML DocBook format.</p></dd><dt><span class="term">--debug</span></dt><dd><p>Works like --tell, bug gives a lot more information.</p></dd><dt><span class="term">--doc</span></dt><dd><p>Define the path to the documentation directory or
+ documentation file. A path can start with
+ <code class="literal">./</code> or <code class="literal">/</code>. Do not use
+ <code class="literal">..</code> in the path. The documentation
+ directory can be a subdirectory of the source directory,
+ or be parallel to the source directory,
+ however they can not be equal. So
+ <span><strong class="command">--src ./sources</strong></span>
+ together with
+ <span><strong class="command">--doc ./documents</strong></span>
+ is fine,
+ but
+ <span><strong class="command">--src ./Everything</strong></span>
+ together with
+ <span><strong class="command">--doc ./Everything</strong></span>
+ is not.
+ </p></dd><dt><span class="term">--doctype_name</span></dt><dd><p>DocBook output requires a <code class="literal"><!DOCTYPE></code> tag.
+ With this option you can specify your own version of it. You have
+ to use it in combination with <span><strong class="command">--doctype_location</strong></span>.
+ An example:
+ </p><pre class="programlisting">
+ robodoc --src test --doc test
+ --doctype_location "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+ --doctype_name docbook-simple/sdocbook.dtd
+ --dbxml
+</pre><p>
+ results in the following docbook file with the following head:
+ </p><pre class="programlisting">
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+"docbook-simple/sdocbook.dtd">
+</pre></dd><dt><span class="term">--doctype_location</span></dt><dd><p>See <span><strong class="command">--doctype_name</strong></span>.</p></dd><dt><span class="term">--headless</span></dt><dd><p>Do not create the head of a document. This allows you to
+ create documents that can be included in other documents, or
+ to which you can add your own style.</p><p>For html output this means that no
+ <code class="literal"><html><head> ..... <body></code>
+ is generated.
+ </p><p>For LaTeX output this means none of the document
+ initialization code is generated, such as
+ <code class="literal">\documentclass{article}</code> or
+ <code class="literal">\begin{document}</code> is generated. If you use
+ this option in combination with <code class="option"> --footless</code>
+ you can use <code class="literal">\include</code> or
+ <code class="literal">\input</code> commands to include the ROBODoc
+ generated documents in a larger document.</p><p>For XML DocBook output this means no
+ <code class="literal"><!DOCTYPE></code>,
+ <code class="literal"><article></code>, and
+ <code class="literal"><articleinfo></code> is generated.
+ </p></dd><dt><span class="term">--first_section_level</span></dt><dd><p>Make the first section start at the specified level
+ instead of 1. This is useful if you want to include the
+ generated documentation in another document.
+ </p></dd><dt><span class="term">--footless</span></dt><dd><p>Do not create the foot of a document. This allows you to
+ create documents that can be included in other documents, or
+ to which you can add your own style.</p><p>For html output this means that no
+ <code class="literal"></body></html></code>
+ is generated.
+ </p><p>For LaTeX output this means no
+ <code class="literal">\end{document}</code> is generated.
+ </p><p>For XML DocBook output this means no
+ <code class="literal"></article></code> is generated.
+ </p></dd><dt><span class="term">--html</span></dt><dd><p>Generate documentation in HTML format.</p></dd><dt><span class="term">--ignore_case_when_linking</span></dt><dd><p>Ignore differences in case when creating cross links.
+ This is handy for languages such as Fortran or Pascal, but
+ in most cases it is better not to use it.</p></dd><dt><span class="term">--internal</span></dt><dd><p>Also include headers marked internal.</p></dd><dt><span class="term">--internalonly</span></dt><dd><p>Only include headers marked internal.</p></dd><dt><span class="term">--index</span></dt><dd><p>Also create a master index file.</p></dd><dt><span class="term">--lock</span></dt><dd><p> Per source file robodoc locks on the first header marker
+ it finds and will recognize only that particular header marker
+ for the remaining part of the file. In addition it locks on
+ the first remark marker in each header and will recognize only
+ that particular remark marker for the remaining part of the
+ header. </p></dd><dt><span class="term">--multidoc</span></dt><dd><p>Generate one document per source file, and copy the
+ directory hierarchy.</p></dd><dt><span class="term">--nosource</span></dt><dd><p>Do not include the SOURCE items.</p></dd><dt><span class="term">--no_subdirectories</span></dt><dd><p>Do not create any subdirectories in the documentation directory
+ instead write all the documentation files in the root directory. The root directory is the one specified with <code class="option">--doc</code>.</p></dd><dt><span class="term">--nodesc</span></dt><dd><p>Do not scan any subdirectories, scan only the top level
+ directory of the source tree.</p></dd><dt><span class="term">--nosort</span></dt><dd><p>Do not sort the headers when generating the
+ documentation. The headers will appear in the same order in
+ the documentation as they appear in the source code.</p></dd><dt><a id="nopre"></a><span class="term"><a id="nopre.term"></a>--nopre</span></dt><dd><p>With this option ROBODoc does not generate preformatted
+ text when creating the item documentation. (Using
+ the <code class="literal"><PRE></code> and
+ <code class="literal"></PRE></code> construction in HTML format
+ for instance). Instead ROBODoc tries to deduce
+ the formatting from the indentation and special
+ characters. See <a href="#formatting" title="3.5. Smart Text Formatting">Smart Text Formatting</a>. This creates much better
+ looking documentation.
+ </p></dd><dt><span class="term">--nogeneratedwith</span></dt><dd><p>Do not add the "generated with robodoc" message at the
+ top of each documentation file.</p></dd><dt><span class="term">--one_file_per_header</span></dt><dd><p>Create a separate documentation file for each header.
+ </p></dd><dt><span class="term">--rc</span></dt><dd><p>Use the specified file instead of <code class="filename">robodoc.rc</code>.
+ </p></dd><dt><span class="term">--rtf</span></dt><dd><p>Generate documentation in RTF format.</p></dd><dt><span class="term">--sections</span></dt><dd><p>Create sections based on the module hierarchy.</p></dd><dt><span class="term">--sectionnameonly</span></dt><dd><p>ROBODoc generates the section headers with names only,
+ no chapter numbers, no parent section names.</p></dd><dt><span class="term">--singledoc</span></dt><dd><p> Define the documentation directory or documentation
+ file.</p></dd><dt><span class="term">--singlefile</span></dt><dd><p> Generate a single document from a single file </p></dd><dt><span class="term">--src</span></dt><dd><p> Define the path for the source directory or source
+ file. The path can start with <code class="literal">./</code> or
+ <code class="literal">/</code>. Do not use <code class="literal">..</code> in the
+ path. </p></dd><dt><span class="term">--tabsize</span></dt><dd><p>Lets you specify the tab size.</p></dd><dt><span class="term">--tabstops</span></dt><dd><p>Specify tab stop locations in a comma separated list.</p><p>
+ Example: <code class="option">--tabstops 10,20,40,80</code>
+ </p></dd><dt><span class="term">--toc</span></dt><dd><p>Add a table of contents. This works in multidoc mode as
+ well as singledoc mode.</p></dd><dt><span class="term">--latex</span></dt><dd><p>Generate documentation in <code class="literal">LaTeX</code> format.</p></dd><dt><span class="term">--tell</span></dt><dd><p>ROBODoc tells you what steps it is taking.</p></dd><dt><span class="term">--documenttitle</span></dt><dd><p>Specify the Title of the whole documentation.</p></dd><dt><span class="term">--altlatex</span></dt><dd><p>Alternate <code class="literal">LaTeX</code> file format
+ (bigger / clearer than normal).
+ </p></dd><dt><span class="term">--latexparts</span></dt><dd><p>Make the first module level as <code class="literal">PART</code>
+ in <code class="literal">LaTeX</code> output
+ (Gives you one more subsection level).</p></dd><dt><a id="syntaxcolors_enable"></a><span class="term"><a id="syntaxcolors_enable.term"></a>--syntaxcolors_enable</span></dt><dd><p>Enable only specific syntax highlighting features in
+ <code class="literal">SOURCE</code> items (<code class="literal">HTML</code> only).</p><p>Usage:
+ <code class="option">--syntaxcolors_enable
+ quotes,squotes,line_comments,block_comments,keywords,non_alpha</code>
+ </p><div class="itemizedlist"><ul type="disc"><li><code class="option">quotes</code>
+ -- Enable highlighting of <code class="literal">"string literals"
+ </code></li><li><code class="option">squotes</code>
+ -- Enable highlighting of <code class="literal">'string literals'
+ </code></li><li><a id="syntaxcolors_enable_line_comments"></a><code class="option"><a id="syntaxcolors_enable_line_comments.option"></a>line_comments</code>
+ -- Enable highlighting of comments that span until the end of lines
+ (See <a href="#linecomments" title="7.17. source line comments block">source line comments block</a>)
+ </li><li><a id="syntaxcolors_enable_block_comments"></a><code class="option"><a id="syntaxcolors_enable_block_comments.option"></a>block_comments</code>
+ -- Enable highlighting of block comments
+ (See <a href="#remark_begin_end" title="7.16.  remark begin markers and remark end markers block">
+remark begin markers and remark end markers block</a>)
+ </li><li><a id="syntaxcolors_enable_keywords"></a><code class="option"><a id="syntaxcolors_enable_keywords.option"></a>keywords</code>
+ -- Enable highlighting of keywords
+ (See <a href="#keywords" title="7.18. keywords block">keywords block</a>)
+ </li><li><code class="option">non_alpha</code>
+ -- Enable highlighting of non alphanumeric characters
+ (like: <code class="literal">#</code>, <code class="literal">$</code>, <code class="literal">%</code>, etc...)
+ </li></ul></div><p>You don't need this if you have the
+ <code class="option"><a href="#cmode">--cmode</a></code>
+ option specified.
+ </p><p> See also the
+ <code class="option"><a href="#syntaxcolors">--syntaxcolors</a></code>
+ option.
+ </p></dd><dt><a id="syntaxcolors"></a><span class="term"><a id="syntaxcolors.term"></a>--syntaxcolors</span></dt><dd><p>Turn on all syntax highlighting features in <code class="literal">SOURCE</code>
+ items (<code class="literal">HTML</code> only).</p><p>This option equals to:
+ <code class="option">--syntaxcolors_enable
+ quotes,squotes,line_comments,block_comments,keywords,non_alpha</code>
+ </p><p>You don't need this if you have the
+ <code class="option"><a href="#cmode">--cmode</a></code>
+ option specified.
+ </p><p> See also the
+ <code class="option"><a href="#syntaxcolors_enable">--syntaxcolors_enable</a></code>
+ option.
+ </p></dd><dt><span class="term">--dotname</span></dt><dd><p>Specify the name (and path / options) of
+ <code class="literal">DOT</code> tool.</p><p>See <a href="#tools" title="8.6. Using external tools">Using external tools</a>.</p></dd><dt><span class="term">--masterindex</span></dt><dd><p>Specify the title and filename of the master index page
+ </p><p>Usage:
+ <code class="option">--masterindex
+ <em class="replaceable"><code>title</code></em>,<em class="replaceable"><code>filename</code></em>
+ </code>
+ </p><p>Example:
+ <code class="option">--masterindex "Index Page,index"</code>
+ </p></dd><dt><span class="term">--sourceindex</span></dt><dd><p>Specify the title and filename of the source files index
+ page</p><p>Usage:
+ <code class="option">--sourceindex
+ <em class="replaceable"><code>title</code></em>,<em class="replaceable"><code>filename</code></em>
+ </code>
+ </p><p>Example:
+ <code class="option">--sourceindex "Source files,sources"</code>
+ </p></dd><dt><span class="term">--header_breaks</span></dt><dd><p>If a header has multiple names, ROBODoc can insert line breaks after every
+ specified number of header names to improve readability. If you do not specify this
+ option, ROBODoc will insert a line break after every two header names. Set it to
+ zero to disable the line breaks.</p><p>
+ Example: <code class="option">--header_breaks 3</code>
+ </p></dd></dl></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="customizing"></a>7. Customizing ROBODoc</h2></div></div></div><p> ROBODoc can be configured with a configuration file called
+ <code class="filename">robodoc.rc</code>. With it you can define item
+ names, frequently used options, and translations for English
+ terms. One should note that if a configuration file is specified,
+ its definitions over-ride ROBODoc internal (i.e. built-in) settings.
+ This is a feature; some arbitrary language may include syntax
+ which conflicts with ROBODoc's internal markers. By taking advantage
+ of a configuration file, these sort of issues and conflicts
+ can be circumvented. An example is shown below.
+</p><pre class="programlisting">
+# Example robodoc.rc
+#
+items:
+ NAME
+ FUNCTION
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+ SIDE EFFECTS
+ HISTORY
+ BUGS
+ EXAMPLE
+ignore items:
+ HISTORY
+ BUGS
+item order:
+ FUNCTION
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+source items:
+ SYNOPSIS
+preformatted items:
+ INPUTS
+ OUTPUTS
+format items:
+ FUNCTION
+ SIDE EFFECTS
+options:
+ --src ./source
+ --doc ./doc
+ --html
+ --multidoc
+ --index
+ --tabsize 8
+headertypes:
+ J "Projects" robo_projects 2
+ F "Files" robo_files 1
+ e "Makefile Entries" robo_mk_entries
+ x "System Tests" robo_syst_tests
+ q Queries robo_queries
+ignore files:
+ README
+ CVS
+ *.bak
+ *~
+ "a test_*"
+accept files:
+ *.c
+ *.h
+ *.pl
+header markers:
+ /****
+ #****
+remark markers:
+ *
+ #
+end markers:
+ ****
+ #****
+header separate characters:
+ ,
+header ignore characters:
+ [
+remark begin markers:
+ /*
+remark end markers:
+ */
+source line comments:
+ //
+keywords:
+ if
+ do
+ while
+ for
+</pre><p>The configuration file consists of a number of blocks.
+ Each block starts with a name followed by a
+ <code class="literal">:</code>.
+ In each block you define a number of values. Each value must
+ start with at least one space.
+</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502679"></a>7.1. items block</h3></div></div></div><p>In this block you can define the names of items that
+ ROBODoc should recognize. Item names can consist of more than
+ one word but should be written in all uppercase characters.
+ Define one item name per line. You do not need to put quotes
+ around them if they contain spaces.
+ </p><p>If you do not define an items block ROBODoc uses its
+ default list of item names. If you define an items block
+ ROBODoc uses only those item names, any of the default items names
+ (except SOURCE) are no longer recognized.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502700"></a>7.2. ignore items block</h3></div></div></div><p>In this block you can define the names of items that
+ ROBODoc should ignore when generating documentation.
+ This can be useful if you want to create documentation
+ for a client, but for instance do not want to include
+ the history items and bugs items.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502714"></a>7.3. item order block</h3></div></div></div><p>In this block you can define the order in which items
+ are to appear in the generated documentation.
+ The items listed here will appear in the given order in the
+ generated documentation. Any remaining items appear
+ after these items in the order they were found in the header.
+ This allows you to make the documentation look more uniform.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sourceitems"></a>7.4. source items block</h3></div></div></div><p>In this block you can define the names of items that
+ ROBODoc handles in the same way as the SOURCE item.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502749"></a>7.5. preformatted items block</h3></div></div></div><p>
+ In this block you can define the names of items that should be always
+ preformatted. This is useful if you have the
+ <code class="option"><a href="#nopre">--nopre</a></code> option
+ specified and want specific items (like input and output lists) to be
+ automatically preformatted. See
+ <a href="#formatting" title="3.5. Smart Text Formatting">Smart Text Formatting</a> for more
+ information.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502783"></a>7.6. format items block</h3></div></div></div><p>
+ In this block you can define the names of items that should be formatted by
+ the <a href="#formatting" title="3.5. Smart Text Formatting">Smart Text Formatting</a> feature
+ (like function descriptions) even if the
+ <code class="option"><a href="#nopre">--nopre</a></code>
+ option has not been specified.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502813"></a>7.7. options block</h3></div></div></div><p>In this block you can define frequently used options.
+ The options you specify here are added to any options you
+ specify on the command line.</p><p>
+ See <a href="#options" title="6. Options">Options</a>.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="headertypes_block"></a>7.8. headertypes block</h3></div></div></div><p>In this block you can define your own header types.
+ These are added to the existing header types. Each new
+ header type requires three parameters: the character used to
+ indicate a header of this type, the title for this type as
+ used in the master index and the name of the file in which the
+ index of this type is stored. If you use a character of an
+ existing header type, this header type is overwritten.
+ </p><p>
+ Additionally the sorting priority can also be specified for each header
+ type. Headers with higher priority will appear earlier in the
+ generated output. (For example you can make module definitions appear
+ at the beginning of a page.) If this parameter is omitted, the header
+ will have the priority 0 (lowest) by default. All pre-defined header
+ types have zero priority, except
+ "<a href="#header_type_h">Header for a module in a project</a>",
+ which has the sorting priority 1.
+ See <a href="#header_types" title="3.2. Header Types">Header Types</a>.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502896"></a>7.9. ignore files block</h3></div></div></div><p>In this block you can define the names of files or
+ directories that ROBODoc should ignore while scanning
+ the directory tree for source files. You can use the
+ wildcard symbols <code class="literal">*</code> and
+ <code class="literal">?</code>. If you use spaces in the expression
+ enclose the expression in quotes.
+ </p><p> For instance, the rc file above causes ROBODoc
+ to skip all <code class="filename">README</code> files, all files with
+ the name <code class="filename">CVS</code> (these are usually
+ directories). It also skips all files with a name that ends
+ with <code class="filename">.bak</code> or <code class="filename">~</code> or
+ that start with <code class="filename">a test_</code> </p><p>Normally you specify the names of directories here
+ and do the filtering of file names with a accept files block.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502963"></a>7.10. accept files block</h3></div></div></div><p>In this block you can define the names of files that
+ robodoc should accept. This test is carried out after the
+ 'ignore files' filtering is performed. Any files that do
+ not match the patterns in this block are ignored by ROBODoc.
+ </p><p>
+ If there is no accept files block all files are accepted.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502981"></a>7.11. header markers block</h3></div></div></div><p>
+ In this block you define what ROBODoc will recognize
+ as start of a header. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2502994"></a>7.12. remark markers block</h3></div></div></div><p>
+ In this block you define what ROBODoc will recognize
+ as start of remark. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2503006"></a>7.13. end markers block</h3></div></div></div><p>
+ In this block you define what ROBODoc will recognize
+ as end of a header. If you use this block ROBODoc only
+ recognizes these markers, any of the inbuilt markers will
+ be ignored.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="separate_characters_block"></a>7.14. header separate characters block</h3></div></div></div><p>
+ In this block you can specify the separation character(s) for multiple
+ header names. The default character is "<code class="literal">,</code>" (comma) if
+ this block is missing. See
+ <a href="#preparing" title="3. Preparing your source code for ROBODoc">Preparing your source code for ROBODoc</a>
+ for more information.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="header_ignore_characters_block"></a>7.15. header ignore characters block</h3></div></div></div><p>
+ In this block you can specify character(s) where the evaluation of a header
+ should stop. The default character is "<code class="literal">[</code>" if this block
+ is missing. This is useful to supply additional information to the header
+ that ROBODoc should not care about.
+ </p><p>
+ For example one can include the version number of the function:
+</p><pre class="programlisting">
+ ****f* financial.library/StealMoney [2.0]
+</pre><p>
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="remark_begin_end"></a>7.16.Â
+remark begin markers and remark end markers block</h3></div></div></div><p>
+ Some languages allow remarks to span more than one line. They
+ start a remark with a begin marker and end it with another
+ marker. For instance in C you can have:
+ </p><pre class="programlisting">
+ /* This is a
+ multi-line remark.
+ */
+</pre><p>
+ Here the markers are <code class="literal">/*</code> and <code class="literal">*/</code>.
+ If you tell ROBODoc what these markers are in a remark begin
+ markers block and remark end markers block. ROBODoc can skip
+ them in source items.</p><p>We illustrate this with an example. Say we have a language
+ that start a remark with <code class="literal">|*</code> and
+ <code class="literal">*|</code>. We define <code class="literal">SYNOPSIS</code>
+ to be a source like item. If we now run ROBODoc on the
+ without a remark begin markers and
+ end markers block on the following piece of source, </p><pre class="programlisting">
+|****f* Bar/foo
+ * FUNCTION
+ * foo computes the foo factor.
+ * SYNOPSIS
+ *|
+int foo( float correction )
+|*
+ * BUGS
+ * None
+ * SOURCE
+ *| .
+{
+ return correction * 42.0;
+}
+|*****|
+</pre><p>the extracted documentation will look like:</p><pre class="literallayout">
+ FUNCTION
+ foo computes the foo factor.
+ SYNOPSIS
+ *|
+ int foo( float correction )
+ |*
+ BUGS
+ None
+ SOURCE
+ *| .
+ {
+ return correction * 42.0;
+ }
+</pre><p>because ROBODoc considers
+ <code class="literal">|*</code> and <code class="literal">*|</code>.to be part of the
+ source, and not part of the begin or end of a header.</p><p> If you add</p><pre class="programlisting">
+remark begin markers:
+ |*
+remark end markers:
+ *|
+</pre><p>the output will look like:</p><pre class="literallayout">
+ FUNCTION
+ foo computes the foo factor.
+ SYNOPSIS
+ int foo( float correction )
+ BUGS
+ None
+ SOURCE
+ {
+ return correction * 42.0;
+ }
+</pre><p>
+ These markers will also be used to highlight block comments if the
+ <code class="option">
+ <a href="#syntaxcolors_enable">--syntaxcolors_enable</a>
+ <a href="#syntaxcolors_enable_block_comments">block_comments</a>
+ </code>
+ option (or the
+ <code class="option">
+ <a href="#syntaxcolors">--syntaxcolors</a>
+ </code>
+ option) is specified (Similar to
+ <a href="#linecomments" title="7.17. source line comments block">source line comments block</a>).
+</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="linecomments"></a>7.17. source line comments block</h3></div></div></div><p>
+ Here you can specify markers which start whole line comments.
+ (Comments that span until the end of line like "<code class="literal">//</code>",
+ "<code class="literal">REM</code>", "<code class="literal">;</code>", etc...)
+ These lines will be highlighted in the generated <code class="literal">HTML</code>
+ output if the
+ <code class="option">
+ <a href="#syntaxcolors_enable">--syntaxcolors_enable</a>
+ <a href="#syntaxcolors_enable_line_comments">line_comments</a>
+ </code>
+ option (or the
+ <code class="option">
+ <a href="#syntaxcolors">--syntaxcolors</a>
+ </code>
+ option) has been specified.</p><p>
+ You don't need this if you have the
+ <code class="option">
+ <a href="#cmode">--cmode</a>
+ </code>
+ option specified.
+ </p><p>
+ The default highlighting color can be changed in the <code class="literal">CSS</code>
+ file (<code class="literal">span.comment</code>).
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="keywords"></a>7.18. keywords block</h3></div></div></div><p>
+ Here you can specify the keywords in your <code class="literal">SOURCE</code> items.
+ These keywords will be highlighted in the generated <code class="literal">HTML</code>
+ output if the
+ <code class="option">
+ <a href="#syntaxcolors_enable">--syntaxcolors_enable</a>
+ <a href="#syntaxcolors_enable_keywords">keywords</a>
+ </code>
+ option (or the
+ <code class="option">
+ <a href="#syntaxcolors">--syntaxcolors</a>
+ </code>
+ option) has been specified. Keywords meant to be the language native ones
+ (<code class="literal">for</code>, <code class="literal">if</code>, etc...).
+ </p><p>
+ You don't need this if you have the
+ <code class="option">
+ <a href="#cmode">--cmode</a>
+ </code>
+ option specified.
+ </p><p>
+ The default highlighting color can be changed in the <code class="literal">CSS</code>
+ file (<code class="literal">span.keyword</code>).
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2503510"></a>7.19. Configuration file location</h3></div></div></div><p>ROBODoc searches the your current directory for the
+ <code class="filename">robodoc.rc</code> file. If it can't find
+ it there it will search in <code class="literal">$HOME/</code>, then
+ <code class="filename">$HOMEDRIVE$HOMEPATH/</code>, and finally in
+ <code class="filename">/usr/share/robodoc/</code>.</p><p>With the <code class="option">--rc</code> option you can tell ROBODoc to
+ use a different file than <code class="filename">robodoc.rc</code> as
+ configuration file. This is handy if you want to create
+ documentation in different formats. For instance: </p><pre class="programlisting">
+robodoc --rc htmlsingle.rc
+robodoc --rc rtfsingle.rc
+robodoc --rc htmlmulti.rc
+</pre></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2504528"></a>8. Tips and Tricks</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504533"></a>8.1. The SOURCE Item</h3></div></div></div><p> With a little extra work you can include part of your
+ source code into your documentation too. The following example
+ shows how this is done:</p><pre class="programlisting">
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ * RB_Panic - Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ * RB_Panic (cause, add_info)
+ * RB_Panic (char *, char *)
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+ void RB_Panic (char *cause, char *add_info)
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</pre><p>You can add a SOURCE item as the last item of your header. Then
+ instead of closing your header with an end marker, you close it
+ normally. The end marker is instead placed at the end of the
+ fragment of source code that you want to include. </p><p>SOURCE items are included by default. If you want to create a
+ document without the SOURCE items use the option
+ <code class="option">--nosource</code>.</p><p>You can also make normal items work like the source item,
+ see <a href="#sourceitems" title="7.4. source items block">source items block</a>.
+</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504567"></a>8.2. Minimizing Duplicate Information</h3></div></div></div><p>It is always good to avoid having the same information in several different locations.
+ It is easy to create headers that have a lot information duplication. Take for instance
+ the following header.
+</p><pre class="programlisting">
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ * RB_Panic - Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ * RB_Panic (cause, add_info)
+ * RB_Panic (char *, char *)
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+ void RB_Panic (char *cause, char *add_info)
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</pre><p>
+ The name <code class="literal">RB_Panic</code> occurs five times. This is tedious to
+ type and difficult to maintain.
+ However with a the right <code class="filename">robodoc.rc</code> this can be changed
+ to:
+</p><pre class="programlisting">
+/****f* Robodoc/RB_Panic [2.0d]
+ * SUMMARY
+ * Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ */
+
+void RB_Panic (char* cause, char *add_info)
+
+/*
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</pre><p><code class="literal">RB_Panic</code> occurs only twice now. In addition changes
+to the function definition only have to be done once.</p><p>The <code class="filename">robodoc.rc</code> required for this is: </p><pre class="programlisting">
+# robodoc.rc file
+items:
+ SUMMARY
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+ SEE ALSO
+ BUGS
+source items:
+ SYNOPSIS
+remark begin markers:
+ /*
+remark end markers:
+ */
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504666"></a>8.3. Advanced formatting with raw HTML and LaTeX code</h3></div></div></div><p> By default an item's body shows up in your documentation in
+ the same way as it is formatted in your source code. All special
+ characters for the output mode are escaped. For instance an <
+ is translated to a &lt; in HTML mode. Sometimes however you
+ like to have some more control of what goes into the
+ documentation. This is possible with the piping. If a line of
+ your item's body starts with one of the special piping markers, the
+ text after this marker is copied verbatim into your documentation.
+ The following example shows how this is done, and how to add
+ equations to your documentation.
+ </p><pre class="programlisting">
+/****m* pipe/pipetest
+ * NAME
+ * pipetest
+ * DESCRIPTION
+ * Simple header to show "piping" features in items.
+ * EXAMPLE
+ * Only "pipes" which match selected output style are picked up.
+ * |html <CENTER>This will be included in <B>HTML</B> output.</CENTER>
+ * |latex \centerline{This will be included in \LaTeX output}
+ * Space is mandatory following the pipe marker. The following is not a
+ * valid pipe marker:
+ * |html<B>Moi!</B>
+ * You should see an equation on the following line:
+ * |html y = x^2 (sorry, plain HTML is not very powerful)
+ * |latex \centerline{$y = x^2$}
+ * How does this look like?
+ * Here comes a multi-line equation array:
+ * |latex \begin{eqnarray}
+ * |latex \frac{\partial u}{\partial \tau} & = & D_u {\nabla}^2 u +
+ * |latex \frac{1}{\epsilon}
+ * |latex \left ( \hat{u}-{\hat{u}}^2-f\, {v} \, \frac{\hat{u}-q}{\hat{u}+q}
+ * |latex \right ) \; , \label{diffspot:1} \\
+ * |latex \frac{\partial v}{\partial \tau} & = & \hat{u}-v \; ,
+ * |latex \label{diffspot:2} \\
+ * |latex \frac{\partial r}{\partial \tau} & = & D_r {\nabla}^2 r \; .
+ * |latex \label{diffspAot:3}
+ * |latex \end{eqnarray}
+ * |html <I>TODO: write this in html</I>
+ * End of the example.
+ ******
+ */
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504752"></a>8.4. Linking to external documents (href, file, mailto, images)</h3></div></div></div><p> In HTML mode ROBODoc recognizes the following links to
+ external documents. </p><div class="itemizedlist"><ul type="disc"><li><p><code class="literal">href:body</code> -- This is replaced with
+ <code class="literal"><a href="body">body</A></code>
+ </p></li><li><p><code class="literal">file:/body</code> -- This is replaced with
+ <code class="literal"><a href="file:/body">file:/body</A></code>
+ </p></li><li><p><code class="literal">mailto:body</code> -- This is replaced with
+ <code class="literal"><a href="mailto:body">body</A></code>
+ </p></li><li><p><code class="literal">http://body</code> -- This is replaced with
+ <code class="literal"><a href="http://body">http://body</A></code>
+ </p></li><li><p><code class="literal">image:body</code> -- This is replaced with
+ <code class="literal"><image src="body"></code>
+ </p></li></ul></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504846"></a>8.5. Linking from an external document</h3></div></div></div><p>To link from an external document to one of the HTML
+ documents generated by ROBODoc you need a label. ROBODoc creates
+ two labels for each header. The first one starts with
+ <code class="literal">robo</code> followed by a number. You can not use
+ this one because the numbers will change each time you run
+ ROBODoc. The second label is an escaped version of the whole
+ header name. In this label all the non alphanumeric characters of
+ the name are replaced by their two digit hexadecimal code.</p><p>An example, if your header name is
+ <code class="literal">Analyser/RB_ToBeAdded</code> the label is
+ <code class="literal">Analyser2fRB5fToBeAdded</code>. Here
+ <code class="literal">/</code> was replaced by <code class="literal">2f</code> and
+ <code class="literal">_</code> was replaced by <code class="literal">5f</code>. As
+ long as you do not change the header name, this label stays the
+ same each time you run ROBODoc.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="tools"></a>8.6. Using external tools</h3></div></div></div><p>
+ You can also execute external tools from <code class="literal">ROBODoc</code> and even
+ pass data to them.
+ The output of these tools can be included in your documentation for instance.
+ </p><p>
+ There are several types of external tools you can use:
+ </p><div class="orderedlist"><ol type="1"><li>Arbitrary tool with passing data through stdin</li><li>Arbitrary tool without passing data through stdin</li><li>The <code class="literal">DOT</code> tool</li></ol></div><p>
+ The following example shows how to use each of them.
+ </p><pre class="programlisting">
+/****m* tools/tooltest
+ *
+ * NAME
+ * Tooltest
+ *
+ * DESCRIPTION
+ * Example showing how to invoke external tools.
+ *
+ * EXAMPLE
+ * This one sorts elements into the file href:nexus-6.txt
+ * The input data is passed through stdin.
+ *
+ * |tool sort > nexus-6.txt
+ * Zhora
+ * Pris
+ * Leon Kowalski
+ * Roy Batty
+ * Rachel
+ * Rick Deckard?
+ * |tool end
+ *
+ * We can also execute tools without having any stdin data.
+ * In the following example the output is simply redirected into href:tears.txt
+ *
+ * |exec echo "All those moments will be lost in time like tears in rain." > tears.txt
+ *
+ * You can also include neat DOT graphs in your documentation.
+ * This one shows a hash table.
+ *
+ * |dot start
+ * digraph G {
+ * nodesep=.05;
+ * rankdir=LR;
+ * node [shape=record,width=.1,height=.1];
+ *
+ * node0 [label = "<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> | ",height=2.0];
+ * node [width = 1.5];
+ * node1 [label = "{<n> n14 | 719 |<p> }"];
+ * node2 [label = "{<n> a1 | 805 |<p> }"];
+ * node3 [label = "{<n> i9 | 718 |<p> }"];
+ * node4 [label = "{<n> e5 | 989 |<p> }"];
+ * node5 [label = "{<n> t20 | 959 |<p> }"] ;
+ * node6 [label = "{<n> o15 | 794 |<p> }"] ;
+ * node7 [label = "{<n> s19 | 659 |<p> }"] ;
+ *
+ * node0:f0 -> node1:n;
+ * node0:f1 -> node2:n;
+ * node0:f2 -> node3:n;
+ * node0:f5 -> node4:n;
+ * node0:f6 -> node5:n;
+ * node2:p -> node6:n;
+ * node4:p -> node7:n;
+ * }
+ * |dot end
+ *
+ ******
+ */
+</pre><p>
+ If you want to use the <code class="literal">DOT</code> tool you need the
+ <code class="literal">Graphviz</code> package.
+ More information and the binaries can be found at
+ <a href="http://www.graphviz.org/" target="_top">
+ <em class="citetitle">http://www.graphviz.org/
+ </em></a>.
+ The created graphs are automatically included in the documentation
+ (<code class="literal">HTML</code> and <code class="literal">LaTeX</code> only).
+ If you generate <code class="literal">PDF</code> output from your <code class="literal">LaTeX</code>
+ file and you want to include <code class="literal">DOT</code> graphs in it, you will also
+ need the <span><strong class="command">epstopdf</strong></span> utility.
+ <code class="literal">ROBODoc</code> lets <code class="literal">DOT</code> generate
+ <code class="literal">PNG</code> images for <code class="literal">HTML</code> output and
+ <code class="literal">PS</code> and <code class="literal">PDF</code> images for
+ <code class="literal">LaTeX</code> output.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2505111"></a>8.7. ROBODoc-ing an existing project</h3></div></div></div><p>
+ The ROBODoc package includes also a standalone binary named
+ <code class="literal">robohdrs</code>.
+ This helper program can take clean source file and insert
+ ROBODoc headers to functions, global variables, and macros.
+ There are issues with this tool but it saves lots of cumbersome typing
+ when starting on documenting an existing code-base with ROBODoc.
+ Type
+<span><strong class="command">
+man robohdrs
+</strong></span>
+ or
+<span><strong class="command">
+robohdrs -h
+</strong></span>
+ for help.
+ Example:
+<span><strong class="command">
+robohdrs -s -p testproj -i "MODIFICATION HISTORY" -i IDEAS testproj.c
+</strong></span>
+</p><p>
+ Note that <code class="literal">robohdrs</code> is supported on UNIX-like platforms only.
+ It requires <code class="literal">fork()</code> and Exuberant Ctags 5.3.1 or newer.
+ </p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2505169"></a>8.8. Using ROBODoc under Windows</h3></div></div></div><p>When you use ROBODoc under windows, don't forget that it is
+ a command line tool. ROBODoc relies on the console window to
+ inform you about problems and errors.</p><p>An easy mistake to make is to create a shortcut to
+ <code class="literal">robodoc.exe</code> and then click on the icon to
+ generate the documentation each time you made some changes to your
+ source code. If you have a fast machine a console window pops up
+ quickly and after that your documentation is ready.</p><p>This works very fine until you make a mistake in one of your
+ headers. The console window still pops up, but before you have a chance
+ to read any of the error messages it is gone again. Most likely
+ you won't even have noticed there were error messages. You will
+ end up with empty documentation or old documentation. </p><p>It is Better to create a batch file with the following commands
+ and to store all the options in a <code class="filename">robodoc.rc</code>
+ file:</p><pre class="programlisting">
+robodoc.exe
+pause
+</pre><p>Now the console window stays open and you have the
+ opportunity to read the error messages.</p><p>While the window is open, right click on the title bar,
+ go to properties->layout and set the buffer size to something
+ like 2500. That way, the next time you run it, you can scroll back
+ and view all error messages.
+ </p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="defaultheaders"></a>9. Languages Supported by Default</h2></div></div></div><p>
+ ROBODoc support a whole range of languages by default.
+ These languages are listed in the following sections.
+ For each language two example headers are shown.
+ One example header without any source items and
+ one example header with source items.
+ </p><p> Any of these markers can be mixed, and they are not limited
+ to the languages listed. So if you have a language that is not
+ listed but that has remarks that start with a <code class="literal">#</code>
+ you can use the Tcl markers, and create headers such as:
+ </p><pre class="programlisting">
+#****f* Foo/Bar
+# FUNCTION
+# Bar snarfs the Foo input and mangles it. Given the right settings
+# it might also do a bit of snu snu.
+#***
+</pre><p>
+ In addition for each language the corresponding entries in
+ the <code class="filename">robodoc.rc</code> are shown.
+ You do not need these, as ROBODoc has these entries
+ built-in. They are shown to make it easier
+ to determine what needs to be specified for languages
+ that ROBODoc does not yet support.
+ </p><p>
+ You can also use these entries if you want to create
+ a <code class="filename">robodoc.rc</code> that supports only a limited
+ number of languages.
+ This because if you specify your own markers, ROBODoc
+ ignores any of the in-built markers.
+ </p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504474"></a>9.1. C</h3></div></div></div><div class="example"><a id="id2504479"></a><p class="title"><b>Example 10. A simple header without any source items in C.</b></p><div class="example-contents"><pre class="programlisting">
+/****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+****
+*/
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2504500"></a><p class="title"><b>Example 11. A header with source items in C.</b></p><div class="example-contents"><pre class="programlisting">
+/****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+* SYNOPSIS
+*/
+int Foo( int fudge )
+/*
+* INPUTS
+* fudge -- the fudge factor
+* SOURCE
+*/
+
+ more source code..
+
+/*****/
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2504516"></a><p class="title"><b>Example 12. The robodoc.rc file required for C if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ /****
+remark markers:
+ *
+end markers:
+ ****
+ /****
+remark begin markers:
+ /*
+remark end markers:
+ */
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2504152"></a>9.2. Modula-2</h3></div></div></div><div class="example"><a id="id2504158"></a><p class="title"><b>Example 13. A simple header without any source items in Modula-2.</b></p><div class="example-contents"><pre class="programlisting">
+(****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+****
+*)
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2504173"></a><p class="title"><b>Example 14. A header with source items in Modula-2.</b></p><div class="example-contents"><pre class="programlisting">
+(****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+* SYNOPSIS
+*)
+int Foo( int fudge )
+(*
+* INPUTS
+* fudge -- the fudge factor
+* SOURCE
+*)
+
+ more source code..
+
+(*****)
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506119"></a><p class="title"><b>Example 15. The robodoc.rc file required for Modula-2 if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ (****
+remark markers:
+ *
+end markers:
+ ****
+ (****
+remark begin markers:
+ (*
+remark end markers:
+ *)
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506135"></a>9.3. Pascal</h3></div></div></div><div class="example"><a id="id2506141"></a><p class="title"><b>Example 16. A simple header without any source items in Pascal.</b></p><div class="example-contents"><pre class="programlisting">
+{****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+****
+*}
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506156"></a><p class="title"><b>Example 17. A header with source items in Pascal.</b></p><div class="example-contents"><pre class="programlisting">
+{****f* ModuleName/Foo
+* FUNCTION
+* Foo computes the foo factor
+* using a fudge factor.
+* SYNOPSIS
+*}
+int Foo( int fudge )
+{*
+* INPUTS
+* fudge -- the fudge factor
+* SOURCE
+*}
+
+ more source code..
+
+{*****}
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506172"></a><p class="title"><b>Example 18. The robodoc.rc file required for Pascal if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ {****
+remark markers:
+ *
+end markers:
+ ****
+ {****
+remark begin markers:
+ {*
+remark end markers:
+ *}
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506189"></a>9.4. C++</h3></div></div></div><div class="example"><a id="id2506194"></a><p class="title"><b>Example 19. A simple header without any source items in C++.</b></p><div class="example-contents"><pre class="programlisting">
+//****f* ModuleName/Foo
+// FUNCTION
+// Foo computes the foo factor
+// using a fudge factor.
+//****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506209"></a><p class="title"><b>Example 20. A header with source items in C++.</b></p><div class="example-contents"><pre class="programlisting">
+//****f* ModuleName/Foo
+// FUNCTION
+// Foo computes the foo factor
+// using a fudge factor.
+// SYNOPSIS
+int Foo( int fudge )
+// INPUTS
+// fudge -- the fudge factor
+// SOURCE
+
+ more source code..
+
+//****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506225"></a><p class="title"><b>Example 21. The robodoc.rc file required for C++ if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ //****
+remark markers:
+ //
+end markers:
+ //****
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506241"></a>9.5. Tcl</h3></div></div></div><div class="example"><a id="id2506246"></a><p class="title"><b>Example 22. A simple header without any source items in Tcl.</b></p><div class="example-contents"><pre class="programlisting">
+#****f* ModuleName/Foo
+# FUNCTION
+# Foo computes the foo factor
+# using a fudge factor.
+#****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506261"></a><p class="title"><b>Example 23. A header with source items in Tcl.</b></p><div class="example-contents"><pre class="programlisting">
+#****f* ModuleName/Foo
+# FUNCTION
+# Foo computes the foo factor
+# using a fudge factor.
+# SYNOPSIS
+int Foo( int fudge )
+# INPUTS
+# fudge -- the fudge factor
+# SOURCE
+
+ more source code..
+
+#****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506277"></a><p class="title"><b>Example 24. The robodoc.rc file required for Tcl if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ #****
+remark markers:
+ #
+end markers:
+ #****
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506293"></a>9.6. Perl</h3></div></div></div><div class="example"><a id="id2506298"></a><p class="title"><b>Example 25. A simple header without any source items in Perl.</b></p><div class="example-contents"><pre class="programlisting">
+#****f* ModuleName/Foo
+# FUNCTION
+# Foo computes the foo factor
+# using a fudge factor.
+#****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506313"></a><p class="title"><b>Example 26. A header with source items in Perl.</b></p><div class="example-contents"><pre class="programlisting">
+#****f* ModuleName/Foo
+# FUNCTION
+# Foo computes the foo factor
+# using a fudge factor.
+# SYNOPSIS
+int Foo( int fudge )
+# INPUTS
+# fudge -- the fudge factor
+# SOURCE
+
+ more source code..
+
+#****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506329"></a><p class="title"><b>Example 27. The robodoc.rc file required for Perl if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ #****
+remark markers:
+ #
+end markers:
+ #****
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506344"></a>9.7. LaTeX/TeX</h3></div></div></div><div class="example"><a id="id2506350"></a><p class="title"><b>Example 28. A simple header without any source items in LaTeX/TeX.</b></p><div class="example-contents"><pre class="programlisting">
+%****f* ModuleName/Foo
+% FUNCTION
+% Foo computes the foo factor
+% using a fudge factor.
+%****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506365"></a><p class="title"><b>Example 29. A header with source items in LaTeX/TeX.</b></p><div class="example-contents"><pre class="programlisting">
+%****f* ModuleName/Foo
+% FUNCTION
+% Foo computes the foo factor
+% using a fudge factor.
+% SYNOPSIS
+int Foo( int fudge )
+% INPUTS
+% fudge -- the fudge factor
+% SOURCE
+
+ more source code..
+
+%****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506381"></a><p class="title"><b>Example 30. The robodoc.rc file required for LaTeX/TeX if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ %****
+remark markers:
+ %
+end markers:
+ %****
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506397"></a>9.8. Postscript</h3></div></div></div><div class="example"><a id="id2506402"></a><p class="title"><b>Example 31. A simple header without any source items in Postscript.</b></p><div class="example-contents"><pre class="programlisting">
+%****f* ModuleName/Foo
+% FUNCTION
+% Foo computes the foo factor
+% using a fudge factor.
+%****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506417"></a><p class="title"><b>Example 32. A header with source items in Postscript.</b></p><div class="example-contents"><pre class="programlisting">
+%****f* ModuleName/Foo
+% FUNCTION
+% Foo computes the foo factor
+% using a fudge factor.
+% SYNOPSIS
+int Foo( int fudge )
+% INPUTS
+% fudge -- the fudge factor
+% SOURCE
+
+ more source code..
+
+%****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506434"></a><p class="title"><b>Example 33. The robodoc.rc file required for Postscript if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ %****
+remark markers:
+ %
+end markers:
+ %****
+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="id2506449"></a>9.9. Occam</h3></div></div></div><div class="example"><a id="id2506455"></a><p class="title"><b>Example 34. A simple header without any source items in Occam.</b></p><div class="example-contents"><pre class="programlisting">
+__****f* ModuleName/Foo
+__ FUNCTION
+__ Foo computes the foo factor
+__ using a fudge factor.
+__****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506470"></a><p class="title"><b>Example 35. A header with source items in Occam.</b></p><div class="example-contents"><pre class="programlisting">
+__****f* ModuleName/Foo
+__ FUNCTION
+__ Foo computes the foo factor
+__ using a fudge factor.
+__ SYNOPSIS
+int Foo( int fudge )
+__ INPUTS
+__ fudge -- the fudge factor
+__ SOURCE
+
+ more source code..
+
+__****
+</pre></div></div><br class="example-break" /><div class="example"><a id="id2506486"></a><p class="title"><b>Example 36. The robodoc.rc file required for Occam if it were not supported by default.</b></p><div class="example-contents"><pre class="programlisting">
+header markers:
+ __****
+remark markers:
+ __
+end markers:
+ __****
+</pre></div></div><br class="example-break" /></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2505449"></a>10. Suggestions and Bugs</h2></div></div></div><p>If you find any bugs, catch them, put them in a jar, and
+ send them to: Frans Slothouber at rfsber {at} xs4all.nl. Suggestions are also welcome at
+ this address. Flames can be directed to the sun.</p></div></div></body></html>
--- /dev/null
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN" "docbook-simple/sdocbook.dtd"
+[
+<!ENTITY bugs SYSTEM "bugs.xml">
+<!ENTITY configuration SYSTEM "configuration.xml">
+<!ENTITY header_examples SYSTEM "header_examples.xml">
+<!ENTITY examples SYSTEM "examples.xml">
+<!ENTITY extracting SYSTEM "extracting.xml">
+<!ENTITY installing SYSTEM "installing.xml">
+<!ENTITY options SYSTEM "options.xml">
+<!ENTITY preface SYSTEM "preface.xml">
+<!ENTITY preparing SYSTEM "preparing.xml">
+<!ENTITY tips SYSTEM "tips.xml">
+] >
+
+<!-- $Id: manual.xml,v 1.63 2007/07/10 19:13:51 gumpu Exp $ -->
+
+<article lang="en">
+
+<articleinfo>
+ <title>ROBODoc 4.99.36 User Manual</title>
+ <authorgroup>
+ <author>
+ <firstname>Frans</firstname>
+ <surname>Slothouber</surname>
+ </author>
+ <author>
+ <firstname>Petteri</firstname>
+ <surname>Kettunen</surname>
+ </author>
+ <author>
+ <firstname>Gergely</firstname>
+ <surname>Budai</surname>
+ </author>
+ </authorgroup>
+ <edition>Users Guide $Revision: 1.63 $</edition>
+ <pubdate>Apr 2007</pubdate>
+ <copyright>
+ <year>1994-2007</year>
+ <holder>
+ Frans Slothouber, Petteri Kettunen,
+ Jacco van Weert, Gergely Budai
+ </holder>
+ </copyright>
+</articleinfo>
+
+&preface;
+<!-- ========== I N S T A L L I N G ======= -->
+&installing;
+<!-- ========== P R E P A R I N G ======= -->
+&preparing;
+<!-- ========== E X T R A C T I N G ======= -->
+&extracting;
+<!-- ========== E X A M P L E S =========== -->
+&examples;
+<!-- ========== O P T I O N S ======= -->
+&options;
+<!-- ========== C O N F I G U R A T I O N -->
+&configuration;
+<!-- ========== T I P S =========== -->
+&tips;
+<!-- ========== H I S T O R Y =========== -->
+<!-- TODO -->
+<!-- ========== A P P E N D I X =============-->
+<section id="defaultheaders">
+ <title id="defaultheaders.title">Languages Supported by Default</title>
+ <para>
+ ROBODoc support a whole range of languages by default.
+ These languages are listed in the following sections.
+ For each language two example headers are shown.
+ One example header without any source items and
+ one example header with source items.
+ </para>
+
+ <para> Any of these markers can be mixed, and they are not limited
+ to the languages listed. So if you have a language that is not
+ listed but that has remarks that start with a <literal>#</literal>
+ you can use the Tcl markers, and create headers such as:
+ </para>
+
+<programlisting>
+#****f* Foo/Bar
+# FUNCTION
+# Bar snarfs the Foo input and mangles it. Given the right settings
+# it might also do a bit of snu snu.
+#***
+</programlisting>
+
+ <para>
+ In addition for each language the corresponding entries in
+ the <filename>robodoc.rc</filename> are shown.
+ You do not need these, as ROBODoc has these entries
+ built-in. They are shown to make it easier
+ to determine what needs to be specified for languages
+ that ROBODoc does not yet support.
+ </para>
+
+ <para>
+ You can also use these entries if you want to create
+ a <filename>robodoc.rc</filename> that supports only a limited
+ number of languages.
+ This because if you specify your own markers, ROBODoc
+ ignores any of the in-built markers.
+ </para>
+ &header_examples;
+</section>
+
+&bugs;
+
+</article>
+
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="/usr/local/src/docbook-xsl-1.72.0/xhtml/docbook.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="/usr/share/docbook-xsl/htmlhelp/htmlhelp.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="/usr/local/src/docbook-xsl-1.72.0/htmlhelp/htmlhelp.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/htmlhelp/htmlhelp.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="e:/docbook/html/docbook.xsl"/>
+ <xsl:param name="html.stylesheet" select="'manual.css'"/>
+ <xsl:param name="admon.graphics" select="1"/>
+ <xsl:param name="section.autolabel" select="1"/>
+</xsl:stylesheet>
+
--- /dev/null
+
+
+
+<section id="options">
+ <title id="options.title">Options</title>
+
+ <para>The behavior of ROBODoc can be further fine-tune with a large number of
+ options.</para>
+
+ <variablelist>
+ <!-- TODO make a varlist from this -->
+ <varlistentry><term>-c</term><listitem>
+ <para>Show the copyright message.</para>
+ </listitem></varlistentry>
+
+ <varlistentry id="cmode"><term id="cmode.term">--cmode</term><listitem>
+ <para>Use ANSI C grammar in SOURCE items and use this
+ for syntax highlighting (<literal>HTML</literal> only).</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--css</term><listitem>
+ <para> Use the content of the specified file to create the
+ <filename>robodoc.css</filename>. The content of the file is
+ copied into <filename>robodoc.css</filename>. </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--dbxml</term><listitem>
+ <para>Generate documentation in XML DocBook format.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--debug</term><listitem>
+ <para>Works like --tell, bug gives a lot more information.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--doc</term><listitem>
+ <para>Define the path to the documentation directory or
+ documentation file. A path can start with
+ <literal>./</literal> or <literal>/</literal>. Do not use
+ <literal>..</literal> in the path. The documentation
+ directory can be a subdirectory of the source directory,
+ or be parallel to the source directory,
+ however they can not be equal. So
+ <command>--src ./sources</command>
+ together with
+ <command>--doc ./documents</command>
+ is fine,
+ but
+ <command>--src ./Everything</command>
+ together with
+ <command>--doc ./Everything</command>
+ is not.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--doctype_name</term><listitem>
+ <para>DocBook output requires a <literal><!DOCTYPE></literal> tag.
+ With this option you can specify your own version of it. You have
+ to use it in combination with <command>--doctype_location</command>.
+ An example:
+ </para>
+<programlisting>
+ robodoc --src test --doc test
+ --doctype_location "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+ --doctype_name docbook-simple/sdocbook.dtd
+ --dbxml
+</programlisting>
+ <para>
+ results in the following docbook file with the following head:
+ </para>
+<programlisting>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+"docbook-simple/sdocbook.dtd">
+</programlisting>
+
+ </listitem></varlistentry>
+
+ <varlistentry><term>--doctype_location</term><listitem>
+ <para>See <command>--doctype_name</command>.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--headless</term><listitem>
+ <para>Do not create the head of a document. This allows you to
+ create documents that can be included in other documents, or
+ to which you can add your own style.</para>
+
+ <para>For html output this means that no
+ <literal><html><head> ..... <body></literal>
+ is generated.
+ </para>
+
+ <para>For LaTeX output this means none of the document
+ initialization code is generated, such as
+ <literal>\documentclass{article}</literal> or
+ <literal>\begin{document}</literal> is generated. If you use
+ this option in combination with <option> --footless</option>
+ you can use <literal>\include</literal> or
+ <literal>\input</literal> commands to include the ROBODoc
+ generated documents in a larger document.</para>
+
+ <para>For XML DocBook output this means no
+ <literal><!DOCTYPE></literal>,
+ <literal><article></literal>, and
+ <literal><articleinfo></literal> is generated.
+ </para>
+
+ </listitem></varlistentry>
+
+ <varlistentry><term>--first_section_level</term><listitem>
+ <para>Make the first section start at the specified level
+ instead of 1. This is useful if you want to include the
+ generated documentation in another document.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--footless</term><listitem>
+ <para>Do not create the foot of a document. This allows you to
+ create documents that can be included in other documents, or
+ to which you can add your own style.</para>
+
+ <para>For html output this means that no
+ <literal></body></html></literal>
+ is generated.
+ </para>
+
+ <para>For LaTeX output this means no
+ <literal>\end{document}</literal> is generated.
+ </para>
+
+ <para>For XML DocBook output this means no
+ <literal></article></literal> is generated.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--html</term><listitem>
+ <para>Generate documentation in HTML format.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--ignore_case_when_linking</term><listitem>
+ <para>Ignore differences in case when creating cross links.
+ This is handy for languages such as Fortran or Pascal, but
+ in most cases it is better not to use it.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--internal</term><listitem>
+ <para>Also include headers marked internal.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--internalonly</term><listitem>
+ <para>Only include headers marked internal.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--index</term><listitem>
+ <para>Also create a master index file.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--lock</term><listitem>
+ <para> Per source file robodoc locks on the first header marker
+ it finds and will recognize only that particular header marker
+ for the remaining part of the file. In addition it locks on
+ the first remark marker in each header and will recognize only
+ that particular remark marker for the remaining part of the
+ header. </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--multidoc</term><listitem>
+ <para>Generate one document per source file, and copy the
+ directory hierarchy.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--nosource</term><listitem>
+ <para>Do not include the SOURCE items.</para>
+ </listitem></varlistentry>
+
+
+ <varlistentry><term>--no_subdirectories</term><listitem>
+ <para>Do not create any subdirectories in the documentation directory
+ instead write all the documentation files in the root directory. The root directory is the one specified with <option>--doc</option>.</para>
+ </listitem></varlistentry>
+
+
+ <varlistentry><term>--nodesc</term><listitem>
+ <para>Do not scan any subdirectories, scan only the top level
+ directory of the source tree.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--nosort</term><listitem>
+ <para>Do not sort the headers when generating the
+ documentation. The headers will appear in the same order in
+ the documentation as they appear in the source code.</para>
+ </listitem></varlistentry>
+
+ <varlistentry id="nopre">
+ <term id="nopre.term">--nopre</term><listitem>
+ <para>With this option ROBODoc does not generate preformatted
+ text when creating the item documentation. (Using
+ the <literal><PRE></literal> and
+ <literal></PRE></literal> construction in HTML format
+ for instance). Instead ROBODoc tries to deduce
+ the formatting from the indentation and special
+ characters. See <xref linkend="formatting"
+ endterm="formatting.title" />. This creates much better
+ looking documentation.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--nogeneratedwith</term><listitem>
+ <para>Do not add the "generated with robodoc" message at the
+ top of each documentation file.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--one_file_per_header</term><listitem>
+ <para>Create a separate documentation file for each header.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--rc</term><listitem>
+ <para>Use the specified file instead of <filename>robodoc.rc</filename>.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--rtf</term><listitem>
+ <para>Generate documentation in RTF format.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--sections</term><listitem>
+ <para>Create sections based on the module hierarchy.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--sectionnameonly</term><listitem>
+ <para>ROBODoc generates the section headers with names only,
+ no chapter numbers, no parent section names.</para>
+ </listitem></varlistentry>
+
+
+ <varlistentry><term>--singledoc</term><listitem>
+ <para> Define the documentation directory or documentation
+ file.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--singlefile</term><listitem>
+ <para> Generate a single document from a single file </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--src</term><listitem>
+ <para> Define the path for the source directory or source
+ file. The path can start with <literal>./</literal> or
+ <literal>/</literal>. Do not use <literal>..</literal> in the
+ path. </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--tabsize</term><listitem>
+ <para>Lets you specify the tab size.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--tabstops</term><listitem>
+ <para>Specify tab stop locations in a comma separated list.</para>
+ <para>
+ Example: <option>--tabstops 10,20,40,80</option>
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--toc</term><listitem>
+ <para>Add a table of contents. This works in multidoc mode as
+ well as singledoc mode.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--latex</term><listitem>
+ <para>Generate documentation in <literal>LaTeX</literal> format.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--tell</term><listitem>
+ <para>ROBODoc tells you what steps it is taking.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--documenttitle</term><listitem>
+ <para>Specify the Title of the whole documentation.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--altlatex</term><listitem>
+ <para>Alternate <literal>LaTeX</literal> file format
+ (bigger / clearer than normal).
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--latexparts</term><listitem>
+ <para>Make the first module level as <literal>PART</literal>
+ in <literal>LaTeX</literal> output
+ (Gives you one more subsection level).</para>
+ </listitem></varlistentry>
+
+ <varlistentry id="syntaxcolors_enable">
+ <term id="syntaxcolors_enable.term">--syntaxcolors_enable</term><listitem>
+ <para>Enable only specific syntax highlighting features in
+ <literal>SOURCE</literal> items (<literal>HTML</literal> only).</para>
+ <para>Usage:
+ <option>--syntaxcolors_enable
+ quotes,squotes,line_comments,block_comments,keywords,non_alpha</option>
+ </para>
+ <para><itemizedlist>
+ <listitem><option>quotes</option>
+ -- Enable highlighting of <literal>"string literals"
+ </literal></listitem>
+ <listitem><option>squotes</option>
+ -- Enable highlighting of <literal>'string literals'
+ </literal></listitem>
+ <listitem id="syntaxcolors_enable_line_comments">
+ <option id="syntaxcolors_enable_line_comments.option">line_comments</option>
+ -- Enable highlighting of comments that span until the end of lines
+ (See <xref linkend="linecomments" endterm="linecomments.title" />)
+ </listitem>
+ <listitem id="syntaxcolors_enable_block_comments">
+ <option id="syntaxcolors_enable_block_comments.option">block_comments</option>
+ -- Enable highlighting of block comments
+ (See <xref linkend="remark_begin_end" endterm="remark_begin_end.title" />)
+ </listitem>
+ <listitem id="syntaxcolors_enable_keywords">
+ <option id="syntaxcolors_enable_keywords.option">keywords</option>
+ -- Enable highlighting of keywords
+ (See <xref linkend="keywords" endterm="keywords.title" />)
+ </listitem>
+ <listitem><option>non_alpha</option>
+ -- Enable highlighting of non alphanumeric characters
+ (like: <literal>#</literal>, <literal>$</literal>, <literal>%</literal>, etc...)
+ </listitem>
+ </itemizedlist></para>
+ <para>You don't need this if you have the
+ <option><xref linkend="cmode" endterm="cmode.term" /></option>
+ option specified.
+ </para>
+ <para> See also the
+ <option><xref linkend="syntaxcolors" endterm="syntaxcolors.term" /></option>
+ option.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry id="syntaxcolors">
+ <term id="syntaxcolors.term">--syntaxcolors</term><listitem>
+ <para>Turn on all syntax highlighting features in <literal>SOURCE</literal>
+ items (<literal>HTML</literal> only).</para>
+ <para>This option equals to:
+ <option>--syntaxcolors_enable
+ quotes,squotes,line_comments,block_comments,keywords,non_alpha</option>
+ </para>
+ <para>You don't need this if you have the
+ <option><xref linkend="cmode" endterm="cmode.term" /></option>
+ option specified.
+ </para>
+ <para> See also the
+ <option><xref linkend="syntaxcolors_enable" endterm="syntaxcolors_enable.term" /></option>
+ option.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--dotname</term><listitem>
+ <para>Specify the name (and path / options) of
+ <literal>DOT</literal> tool.</para>
+ <para>See <xref linkend="tools" endterm="tools.title" />.</para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--masterindex</term><listitem>
+ <para>Specify the title and filename of the master index page
+ </para>
+ <para>Usage:
+ <option>--masterindex
+ <replaceable>title</replaceable>,<replaceable>filename</replaceable>
+ </option>
+ </para>
+ <para>Example:
+ <option>--masterindex "Index Page,index"</option>
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--sourceindex</term><listitem>
+ <para>Specify the title and filename of the source files index
+ page</para>
+ <para>Usage:
+ <option>--sourceindex
+ <replaceable>title</replaceable>,<replaceable>filename</replaceable>
+ </option>
+ </para>
+ <para>Example:
+ <option>--sourceindex "Source files,sources"</option>
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>--header_breaks</term><listitem>
+ <para>If a header has multiple names, ROBODoc can insert line breaks after every
+ specified number of header names to improve readability. If you do not specify this
+ option, ROBODoc will insert a line break after every two header names. Set it to
+ zero to disable the line breaks.</para>
+ <para>
+ Example: <option>--header_breaks 3</option>
+ </para>
+ </listitem></varlistentry>
+
+ </variablelist>
+
+</section>
+
+
--- /dev/null
+<!-- $Id: preface.xml,v 1.4 2007/05/11 08:16:44 thuffir Exp $ -->
+<section>
+ <title>Preface</title>
+
+ <para>ROBODoc is an API documentation tool for C, C++, Java, Assembler,
+ Basic, Fortran, LaTeX, Postscript, Tcl/Tk, LISP, Forth, Perl, Shell
+ Scripts, Makefiles, Occam, COBOL, DCL, Visual Basic, HTML, DB/C, XML,
+ and many other languages. It can be made to work with any language that
+ supports comments.</para>
+
+ <para>ROBODoc works by extracting specially formatted headers from your
+ source code and writes these to documentation files. These files can be
+ formatted in HTML, ASCII, XML DocBook, or RTF; and indirectly to
+ PDF and HTML Help.</para>
+
+ <para>ROBODoc is similar to JavaDoc, though the idea is much
+ older than JavaDoc. ROBODoc allows you to maintain a program and
+ its documentation in a single file. This makes it easier to keep
+ your documentation up-to-date.</para>
+
+ <para>ROBODoc can be used to document anything you like,
+ functions, methods, variables, definitions, test cases, makefile
+ entries, and anything else you can think of.</para>
+
+ <para>It can create documentation consisting of many small files.
+ For instance in HTML format for easy browsing and publication on
+ the web. It can also create a single file in LaTeX or RTF format
+ for inclusion into bigger design documents. The RTF format is
+ suited to be included in Word documents.</para>
+
+ <para>ROBODoc allows you to separate internal documentation from
+ external documentation. In singledoc mode it can create a section
+ layout based on the hierarchy of your modules.</para>
+
+ <para>ROBODoc is designed to work with a lot of different
+ programming languages. It has no knowledge of the syntax of
+ programming languages. It only has some knowledge about how
+ comments start and end in a lot of programming languages. This
+ means that you sometimes have to do a little more work compared to
+ other tools that have detailed knowledge of the syntax of a
+ particular language. They can use that knowledge to figure out
+ some of the information automatically. This usually also means
+ that they work only with one or two languages. </para>
+
+</section>
+
--- /dev/null
+<!-- $Id: preparing.xml,v 1.5 2007/05/11 08:16:44 thuffir Exp $ -->
+
+<section id="preparing">
+<title id="preparing.title">Preparing your source code for ROBODoc</title>
+
+ <para> ROBODoc allows you to mix the program documentation with
+ the source code. It does require though that this documentation
+ has a particular layout so ROBODoc can recognize it. There are
+ three key concepts: headers, items, and sections. </para>
+
+<section>
+<title>Headers</title>
+
+ <para>Headers are the building blocks of the documentation. Lets
+ look at an example. The following header was taken from the
+ documentation of the predecessor of ROBODoc, AutoDoc.</para>
+
+<example id="robodocheader">
+ <title>A ROBODoc header in C.</title>
+<programlisting>
+ /****f* financial.library/StealMoney
+ * NAME
+ * StealMoney -- Steal money from the Federal Reserve Bank. (V77)
+ * SYNOPSIS
+ * error = StealMoney( userName, amount, destAccount, falseTrail )
+ * FUNCTION
+ * Transfer money from the Federal Reserve Bank into the
+ * specified interest-earning checking account. No records of
+ * the transaction will be retained.
+ * INPUTS
+ * userName - name to make the transaction under. Popular
+ * favorites include "Ronald Reagan" and
+ * "Mohamar Quadaffi".
+ * amount - Number of dollars to transfer (in thousands).
+ * destAccount - A filled-in AccountSpec structure detailing the
+ * destination account (see financial/accounts.h).
+ * If NULL, a second Great Depression will be
+ * triggered.
+ * falseTrail - If the DA_FALSETRAIL bit is set in the
+ * destAccount, a falseTrail structure must be
+ * provided.
+ * RESULT
+ * error - zero for success, else an error code is returned
+ * (see financial/errors.h).
+ * EXAMPLE
+ * Federal regulations prohibit a demonstration of this function.
+ * NOTES
+ * Do not run on Tuesdays!
+ * BUGS
+ * Before V88, this function would occasionally print the
+ * address and home phone number of the caller on local police
+ * 976 terminals. We are confident that this problem has been
+ * resolved.
+ * SEE ALSO
+ * CreateAccountSpec(),security.device/SCMD_DESTROY_EVIDENCE,
+ * financial/misc.h
+ ******
+ * You can use this space for remarks that should not be included
+ * in the documentation.
+ */
+</programlisting>
+</example>
+ <para>A header consists of three different elements: a
+ begin marker, a number of items, and an end marker. The begin marker
+ in the example is:</para>
+
+<programlisting>
+ ****f* financial.library/StealMoney
+</programlisting>
+
+<para>It marks the beginning of a header. It also tells ROBODoc
+</para>
+<itemizedlist>
+ <listitem><para>the name of the element that is being documented, StealMoney,</para></listitem>
+ <listitem><para>the module it is part of, financial.library,</para></listitem>
+ <listitem><para>the kind of element, <literal>f</literal>, which stands for function.</para></listitem>
+</itemizedlist>
+
+<para>
+ ROBODoc always expects that a <literal>/</literal> separates the module name and an element name.
+ So <literal>ModFoo/funcBar</literal>
+ is a valid name, but <literal>funcBar</literal> is not.
+ See <xref linkend="sections" endterm="sections.title" /> for more
+ information.
+</para>
+
+<para>
+ Names can also contain spaces but ROBODoc won't create links to names with
+ embedded spaces.
+</para>
+
+<para>
+ You can also have multiple names for a header. This is useful if you
+ document similar objects together in one header (for example assembly
+ subroutines with multiple jump-in points). Multiple names are separated by
+ commas and can span over more than one line.
+<programlisting>
+ ****f* financial.library/StealMoney, Steal_Money
+</programlisting>
+ In the above example all references found to <literal>StealMoney</literal>
+ and <literal>Steal_Money</literal> in other headers will be automatically
+ linked to this header.
+ The separation character(s) can be specified by the
+ <xref linkend="separate_characters_block" endterm="separate_characters_block.title" />.
+ See <xref linkend="customizing" endterm="customizing.title" /> for more
+ information.
+</para>
+
+<para>
+The end marker:
+</para>
+
+<programlisting>
+ ******
+</programlisting>
+
+ <para>
+ marks the end of a header.
+ </para>
+
+ <para>Items begin with an item name and are followed by the
+ item's body. An example: </para>
+
+<programlisting>
+ * FUNCTION
+ * Transfer money from the Federal Reserve Bank into the
+ * specified interest-earning checking account. No records of
+ * the transaction will be retained.
+</programlisting>
+
+ <para>
+ In this case the item's name is FUNCTION.
+ </para>
+
+ <para>
+ Each line of an item starts with a remark marker. In this case
+ <literal>*</literal>.
+ </para>
+
+<para>That what ROBODoc needs to recognize a header is therefore:</para>
+
+<example>
+ <title>The markers needed by ROBODoc to recognize a header.</title>
+<programlisting>
+ /****f* financial.library/StealMoney
+ * NAME
+ *
+ * SYNOPSIS
+ *
+ * FUNCTION
+ *
+ *
+ *
+ * INPUTS
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * RESULT
+ *
+ *
+ * EXAMPLE
+ *
+ * NOTES
+ *
+ * BUGS
+ *
+ *
+ *
+ *
+ * SEE ALSO
+ *
+ *
+ ******
+</programlisting>
+</example>
+
+ <para>
+ The above example is in C. ROBODoc supports many more
+ languages though. See <xref linkend="defaultheaders" endterm="defaultheaders.title" />.
+ </para>
+
+</section>
+
+
+<section id="header_types">
+<title id="header_types.title">Header Types</title>
+
+ <para> ROBODoc defines a number of header types. You don't need
+ to use them but they can be useful for sorting information. The
+ header type tells ROBODoc what kind of object you are documenting.
+ This information allows ROBODoc to create more useful index
+ tables.</para>
+
+ <para>The type is identified by one or two characters. ROBODoc
+ expects to find them after the fourth <literal>*</literal> in the
+ begin marker. So <literal>#****f</literal> is a valid marker,
+ but <literal>#**f**</literal> is not.</para>
+
+ <para>If a single character is given, the type is defined as
+ listed in the following table</para>
+
+<table frame="all"><title>Default header types</title>
+ <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1'/>
+ <colspec colname='c2'/>
+ <tbody>
+ <row><entry>c</entry><entry>Header for a class </entry></row>
+ <row><entry>d</entry><entry>Header for a constant (from define)</entry> </row>
+ <row><entry>f</entry><entry>Header for a function</entry></row>
+ <row><entry>h</entry>
+ <entry id="header_type_h">Header for a module in a project</entry></row>
+ <row><entry>m</entry><entry>Header for a method</entry> </row>
+ <row><entry>s</entry><entry>Header for a structure</entry> </row>
+ <row><entry>t</entry><entry>Header for a types</entry> </row>
+ <row><entry>u</entry><entry>Header for a unit test</entry> </row>
+ <row><entry>v</entry><entry>Header for a variable</entry> </row>
+ <row><entry>*</entry><entry>Generic header for everything else</entry> </row>
+ </tbody>
+ </tgroup>
+</table>
+
+ <para>If two characters are given, the first character should be
+ <literal>i</literal> and the second can be any of the other
+ characters from the table above. This creates an internal header
+ of the type specified by the second character. Internal headers
+ are special. They can be used to hide certain headers. They are
+ only extracted if requested. You can use them to document internal
+ functions, classes, etc. that you do not want clients to see,
+ creating what might be a programmer's manual as opposed to a
+ user's manual.</para>
+
+ <para>So <literal>/****if* Module/func1</literal> defines an
+ internal function called <literal>func1</literal>.
+ </para>
+
+ <para>Headers marked internal are by default not included in the
+ generated documentation. If you want to include them use the
+ option <option>--internal</option>. You can also generate the
+ documentation from only the internal headers with the option
+ <option>--internalonly</option>.
+ </para>
+
+ <para>You can define your own header types using the ROBODoc
+ configuration file, <filename>robodoc.rc</filename>.
+ See <xref linkend="headertypes_block" endterm="headertypes_block.title" />.
+ This way you can document anything you like, for instance makefile
+ entries, system tests, or exceptions.
+ </para>
+
+</section>
+
+<section> <title>Items</title>
+
+ <para> By default ROBODoc recognizes the following items: </para>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ NAME
+ </term>
+ <listitem>
+ <para>Item name plus a short description.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ COPYRIGHT
+ </term>
+ <listitem>
+ <para>Who own the copyright : "(c) <year>-<year> by
+ <company/person>"
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ SYNOPSIS
+ </term>
+ <term>
+ USAGE
+ </term>
+ <listitem>
+ <para>
+ How to use it.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ FUNCTION
+ </term>
+ <term>
+ DESCRIPTION
+ </term>
+ <term>
+ PURPOSE
+ </term>
+ <listitem>
+ <para>
+ What does it do.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ AUTHOR
+ </term>
+ <listitem>
+ <para>
+ Who wrote it.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ CREATION DATE
+ </term>
+ <listitem>
+ <para>When did the work start.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ MODIFICATION HISTORY
+ </term>
+ <term>
+ HISTORY
+ </term>
+ <listitem>
+ <para>Who has done which changes and when.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ INPUTS
+ </term>
+ <term>
+ ARGUMENTS
+ </term>
+ <term>
+ OPTIONS
+ </term>
+ <term>
+ PARAMETERS
+ </term>
+ <term>
+ SWITCHES
+ </term>
+ <listitem>
+ <para>
+ What can we feed into it.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ OUTPUT
+ </term>
+ <term>
+ SIDE EFFECTS
+ </term>
+ <listitem>
+ <para>What output is made.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ RESULT
+ </term>
+ <term>
+ RETURN VALUE
+ </term>
+ <listitem>
+ <para>
+ What do we get returned.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ EXAMPLE
+ </term>
+ <listitem>
+ <para>
+ A clear example of the items use.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ NOTES
+ </term>
+ <listitem>
+ <para>
+ Any annotations
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ DIAGNOSTICS
+ </term>
+ <listitem>
+ <para> Diagnostic output.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ WARNINGS
+ </term>
+ <term>
+ ERRORS
+ </term>
+ <listitem>
+ <para>Warning and error-messages.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ BUGS
+ </term>
+ <listitem>
+ <para>Known bugs.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ TODO
+ </term>
+ <term>
+ IDEAS
+ </term>
+ <listitem><para>What to implement next and ideas. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ PORTABILITY
+ </term>
+ <listitem><para>Where does it come from, where will it work. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ SEE ALSO
+ </term>
+ <listitem><para>References to other functions, man pages, other documentation. </para></listitem>
+ </varlistentry>
+
+
+ <varlistentry>
+ <term>
+ METHODS
+ </term>
+ <term>
+ NEW METHODS
+ </term>
+ <listitem><para>OOP methods.</para></listitem>
+
+ </varlistentry>
+
+
+ <varlistentry>
+ <term>
+ ATTRIBUTES
+ </term>
+ <term>
+ NEW ATTRIBUTES
+ </term>
+ <listitem><para>OOP attributes.</para></listitem>
+ </varlistentry>
+
+
+ <varlistentry>
+ <term>
+ TAGS
+ </term>
+ <listitem>
+ <para>Tag-item description.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ DERIVED FROM
+ </term>
+ <listitem><para>OOP super class. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ DERIVED BY
+ </term>
+ <listitem><para>OOP sub class. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ USES
+ </term>
+ <term>
+ CHILDREN
+ </term>
+ <listitem><para>What modules are used by this one. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ USED BY
+ </term>
+ <term>
+ PARENTS
+ </term>
+ <listitem><para>Which modules do use this one. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ COMMANDS
+ </term>
+ <listitem><para>Command description. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ SOURCE
+ </term>
+ <listitem><para>Source code inclusion. </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>You can define your own items using the ROBODoc
+ configuration file, <filename>robodoc.rc</filename>. See <xref
+ linkend="customizing" endterm="customizing.title" />. </para>
+
+</section>
+
+<section id="sections">
+<title id="sections.title">Sections</title>
+
+ <para>The structure of source code for a project is usually
+ hierarchical. A project might consists of several applications,
+ an application of several modules, a module of several functions
+ or even sub modules. ROBODoc allows you to show this hierarchy in
+ your documentation. For this you specify the hierarchy in the
+ header name. For instance, you have a project that is going to
+ create a new language called D. The D Language project might
+ consists of three applications: a preprocessor, a compiler, and a
+ linker. The compiler consists of two modules, a parser and a
+ generator. The parser module consists of several
+ functions.</para>
+
+ <para>The following three headers show how this hierarchy can be
+ defined in the header name.</para>
+
+<programlisting>
+#****h* D-Language/Compiler
+# FUNCTION
+# The compiler takes a preprocessed source file and
+# turns it into an object file.
+#***
+</programlisting>
+
+<programlisting>
+#****h* D-Language/Linker
+# FUNCTION
+# The linker module contains functions that scan a
+# object file and build the executable.
+#***
+</programlisting>
+
+<programlisting>
+#****h* Compiler/Parser
+# FUNCTION
+# The parser module contains functions that scan a
+# preprocessed source file and build the syntax tree.
+#***
+</programlisting>
+
+<programlisting>
+#****f* Parser/ReadToken
+# FUNCTION
+# ReadToken reads the next token from the input
+# file.
+#***
+</programlisting>
+
+ <para>When you generate documentation with the option
+ <option>--section</option>, ROBODoc uses the hierarchical
+ information when generating the table of contents and document
+ section information. For instance in HTML sections are started
+ with <H1>, <H2>, <H3> depending on the level
+ in the hierarchy. The table of contents will also contain levels. The
+ table of contents for the above example will be: </para>
+
+<programlisting>
+1. D-Language/Compiler
+1.1 Compiler/Parser
+1.1.1 Parser/ReadToken
+2. D-Language/Linker
+</programlisting>
+
+</section>
+
+<section id="formatting"> <title id="formatting.title">Smart Text Formatting</title>
+
+<para>By default ROBODoc creates preformatted text in the output
+documentation for all the text it finds in an item. This means
+that the formatting of the output looks the same as the formatting of
+the text of an item. Line-breaks and indentation stay the same.
+This is easy but does not always create the best looking
+output.</para>
+
+<para>ROBODoc can also try to deduce the formatting of your text based
+on the layout and indentation of your text and on special characters in the text.
+It works a bit similar to the input method of Wikis. In the context of this
+manual this is called Smart Formatting.
+</para>
+
+<para>You switch this on with the option <option>--nopre</option>.
+ROBODoc now tries to find three kind of elements: paragraphs,
+lists, and preformatted text.</para>
+
+<para>Paragraphs are separated by empty lines. So the following item
+has two paragraphs.</para>
+
+<example>
+ <title>Two paragraphs.</title>
+<programlisting>
+ FUNCTION
+ This function does something.
+
+ And it then does something else
+ and a bit more.
+</programlisting>
+</example>
+
+<para> A List starts with a line that ends with a ':' which is then
+followed by one or more list items. List items should start with '*',
+'-', or 'o'. So the following item contains a valid list: </para>
+
+<example>
+ <title>A list.</title>
+<programlisting>
+ FUNCTION
+ This function does:
+ * a lot of foo
+ * some snafuing
+ * and a bit of foobarring.
+</programlisting>
+</example>
+
+<para> A list item can span more than one line if the second and following
+lines are indented. So this is also a valid list:</para>
+
+<example>
+ <title>A list where one of the items spans more than one line.</title>
+<programlisting>
+ FUNCTION
+ This function does:
+ * a lot of foo and preprocessing of the
+ raw input with the aid
+ of the some magic
+ * some snafuing
+ * and a bit of foobarring.
+</programlisting>
+</example>
+
+
+<para>If list items directly follow the name of a robodoc item they
+also form a valid list. So this is a valid list:</para>
+
+<example>
+ <title>an implicit list</title>
+<programlisting>
+ INPUTS
+ * inputname -- the name of the input file
+ * outputname -- the name of the output file
+ * n -- the number of characters to be written
+</programlisting>
+</example>
+
+<para> Preformatted text is indicated by indenting it more that the
+surrounding text. The first non-empty line in an item determines the
+base indenting. Any lines with an indentation larger than this are
+preformatted. An example:</para>
+
+<example>
+ <title>Preformatted text</title>
+<literallayout class="monospaced">
+ FUNCTION
+ The following lines are preformatted
+ +--------+
+ | A box |
+ | |
+ +--------+
+ End of the preformatted text.
+</literallayout>
+</example>
+
+<para>The following is a complete example.</para>
+
+<example id="fullsmart">
+ <title>All three elements of smart formatting.</title>
+<literallayout class="monospaced">
+ This is some example text.
+ And some more.
+
+ This is even more, and we start a list:
+ * a list item
+ * a list item
+ * a list item
+
+ And we can also do preformatted stuff
+ by indenting
+ +--------+
+ | |
+ +--------+
+ The box will stay.
+</literallayout>
+</example>
+
+<para>will be turn into</para>
+
+<example id="fulloutput">
+ <title>The corresponding HTML output.</title>
+<literallayout class="monospaced">
+ <p>This is some example text.
+ And some more.</p>
+
+ <p>This is even more, and we start a list:</p>
+ <ul>
+ <li>a list item</li>
+ <li>a list item</li>
+ <li>a list item</li>
+ </ul>
+
+ <p>And we can also do preformatted stuff
+ by indenting</p>
+ <pre>
+ +--------+
+ | |
+ +--------+
+ </pre>
+ <p> The box will stay.</p>
+</literallayout>
+</example>
+
+</section>
+
+</section>
+
+
--- /dev/null
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH ROBODoc "1" "Jul 2006" "ROBODoc 4.99.36"
+
+.SH NAME
+ROBODoc \- Extract documentation from source code.
+
+.SH SYNOPSIS
+.B robodoc
+.I --src
+<source>
+.I --doc
+<destination>
+.B [options]
+
+.SH DESCRIPTION
+
+ROBODoc extracts specially formated documentation from the source code.
+It allows you to maintain a program and its documentation in a single
+file.
+
+.SH OPTIONS
+
+.IP -c
+Show the copyright message.
+
+.IP --cmode
+Use ANSI C grammar in SOURCE items and use this for some syntax
+highlighting (HTML only).
+
+.IP --doc
+Define the documentation directory or documentation file.
+A directory should start with a '/' or a './' and
+should not include any '..'.
+
+.IP --src
+Define the source directory or source file. A directory should start
+with a '/' or a './' and should not include any '..'.
+
+.IP --ascii
+Generate documentation in ASCII format.
+
+.IP --html
+Generate documentation in HTML format.
+
+.IP --css
+Use to content of the specified file to create the
+.IR robodoc.css .
+The content of the file is copied into
+.IR robodoc.css .
+
+.IP --dbxml
+Generate documentation in XML DocBook format.
+
+.IP --latex
+Generate documentation in LaTeX format.
+
+.IP --rtf
+Generate documentation in RTF format.
+
+.IP --index
+Create an additional master index file.
+
+.IP --multidoc
+Store all documentation in seperate files.
+
+.IP --singledoc
+Store all documentation in a single file.
+
+.IP --singlefile
+Generate a single document from a single file
+
+.IP --toc
+Add a table of contents. This works in multidoc mode as well as singledoc mode.
+
+.IP --internal
+Also include headers marked internal.
+
+.IP --internalonly
+Only include headers marked internal.
+
+.IP --ignore_case_when_linking
+Ignore the case of the symbols when trying to find crosslinks.
+In this mode FOO and Foo will be linked to foo.
+
+.IP --footless
+Do not create the food of a document.
+
+.IP --headless
+Do not create the head of a document.
+
+.IP --lock
+Per source file
+.B ROBODoc
+locks on the first headermarker it finds and will recognize only that
+particular headermarker for the remaining part of the file. In addition
+it locks on the first remark marker in each header and will recognize
+only that particular remark marker for the remaining part of the header.
+
+.IP --nosource
+Do not include the SOURCE items.
+
+.IP --nodesc
+Do not scan any subdirectories, scan only the top level directory
+of the source tree.
+
+.IP --nopre
+Do not use <PRE> and </PRE> in the HTML output, instead based on
+the layout of the text create paragraphs, item lists, and
+preformatted sections.
+
+.IP --rc
+Use the specified file instead of
+.IR robodoc.rc .
+Note that definitions in a configuration file over-ride
+built-in settings.
+
+.IP --sections
+Create sections based on the module hierarchy.
+
+.IP --tabsize
+Lets you specify the tabsize.
+
+.IP --tell
+.B ROBODoc
+tells you about every step it takes.
+
+.IP --version
+Print version information and exit.
+
+.SH EXAMPLES
+
+.EX
+robodoc --src ./ --doc myproject --singledoc --html
+.EE
+
+Creates a document called myproject.html from all source files
+in the current directory.
+
+.EX
+robodoc --src ./myproject/
+ --doc ./myprojectdoc --multidoc --html --index
+.EE
+
+Creates seperate documents for all the source files found in
+the directory ./myproject they are stored in the directory ./myprojectdoc.
+A master index file called ./myprojectdoc/masterindex.html is also
+created. This provides links to all the individual files.
+
+.SH FILES
+.SS "Example headers"
+.PP
+.nf
+\fI/usr/local/share/doc/robodoc/Examples\fR
+.fi
+
+.SH COPYRIGHT
+
+Copyright \(co 1994-2003 Frans Slothouber, Petteri Kettunen, and Jacco van Weert.
+
+.br
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+.SH AUTHORS
+
+Frans Slothouber, Jacco van Weert, Petteri Kettunen, Bernd Koesling,
+Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner, Sasha Vasko,
+Nathan Prewitt and Dennis Stampfer.
+
+.SH SEE ALSO
+
+The documentation in HTML format that comes with ROBODoc. Latest
+version can be found from http://www.xs4all.nl/~rfsber/Robo/ or from
+http://sourceforge.net/projects/robodoc/.
+
+.BR robohdrs(1)
--- /dev/null
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH ROBODoc "1" "Jul 2006" "ROBODoc 4.99.28"
+
+.SH NAME
+ROBODoc \- Extract documentation from source code.
+
+.SH SYNOPSIS
+.B robodoc
+.I --src
+<source>
+.I --doc
+<destination>
+.B [options]
+
+.SH DESCRIPTION
+
+ROBODoc extracts specially formated documentation from the source code.
+It allows you to maintain a program and its documentation in a single
+file.
+
+.SH OPTIONS
+
+.IP -c
+Show the copyright message.
+
+.IP --cmode
+Use ANSI C grammar in SOURCE items and use this for some syntax
+highlighting (HTML only).
+
+.IP --doc
+Define the documentation directory or documentation file.
+A directory should start with a '/' or a './' and
+should not include any '..'.
+
+.IP --src
+Define the source directory or source file. A directory should start
+with a '/' or a './' and should not include any '..'.
+
+.IP --ascii
+Generate documentation in ASCII format.
+
+.IP --html
+Generate documentation in HTML format.
+
+.IP --css
+Use to content of the specified file to create the
+.IR robodoc.css .
+The content of the file is copied into
+.IR robodoc.css .
+
+.IP --dbxml
+Generate documentation in XML DocBook format.
+
+.IP --latex
+Generate documentation in LaTeX format.
+
+.IP --rtf
+Generate documentation in RTF format.
+
+.IP --index
+Create an additional master index file.
+
+.IP --multidoc
+Store all documentation in seperate files.
+
+.IP --singledoc
+Store all documentation in a single file.
+
+.IP --singlefile
+Generate a single document from a single file
+
+.IP --toc
+Add a table of contents. This works in multidoc mode as well as singledoc mode.
+
+.IP --internal
+Also include headers marked internal.
+
+.IP --internalonly
+Only include headers marked internal.
+
+.IP --ignore_case_when_linking
+Ignore the case of the symbols when trying to find crosslinks.
+In this mode FOO and Foo will be linked to foo.
+
+.IP --footless
+Do not create the food of a document.
+
+.IP --headless
+Do not create the head of a document.
+
+.IP --lock
+Per source file
+.B ROBODoc
+locks on the first headermarker it finds and will recognize only that
+particular headermarker for the remaining part of the file. In addition
+it locks on the first remark marker in each header and will recognize
+only that particular remark marker for the remaining part of the header.
+
+.IP --nosource
+Do not include the SOURCE items.
+
+.IP --nodesc
+Do not scan any subdirectories, scan only the top level directory
+of the source tree.
+
+.IP --nopre
+Do not use <PRE> and </PRE> in the HTML output, instead based on
+the layout of the text create paragraphs, item lists, and
+preformatted sections.
+
+.IP --rc
+Use the specified file instead of
+.IR robodoc.rc .
+Note that definitions in a configuration file over-ride
+built-in settings.
+
+.IP --sections
+Create sections based on the module hierarchy.
+
+.IP --tabsize
+Lets you specify the tabsize.
+
+.IP --tell
+.B ROBODoc
+tells you about every step it takes.
+
+.IP --version
+Print version information and exit.
+
+.SH EXAMPLES
+
+.EX
+robodoc --src ./ --doc myproject --singledoc --html
+.EE
+
+Creates a document called myproject.html from all source files
+in the current directory.
+
+.EX
+robodoc --src ./myproject/
+ --doc ./myprojectdoc --multidoc --html --index
+.EE
+
+Creates seperate documents for all the source files found in
+the directory ./myproject they are stored in the directory ./myprojectdoc.
+A master index file called ./myprojectdoc/masterindex.html is also
+created. This provides links to all the individual files.
+
+.SH FILES
+.SS "Example headers"
+.PP
+.nf
+\fI@prefix@/share/doc/robodoc/Examples\fR
+.fi
+
+.SH COPYRIGHT
+
+Copyright \(co 1994-2003 Frans Slothouber, Petteri Kettunen, and Jacco van Weert.
+
+.br
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+.SH AUTHORS
+
+Frans Slothouber, Jacco van Weert, Petteri Kettunen, Bernd Koesling,
+Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner, Sasha Vasko,
+Nathan Prewitt and Dennis Stampfer.
+
+.SH SEE ALSO
+
+The documentation in HTML format that comes with ROBODoc. Latest
+version can be found from http://www.xs4all.nl/~rfsber/Robo/ or from
+http://sourceforge.net/projects/robodoc/.
+
+.BR robohdrs(1)
--- /dev/null
+#
+# $Id: robodoc.rc,v 1.3 2006/08/21 19:15:58 gumpu Exp $
+#
+# Example robodoc.rc file.
+#
+options:
+ --doc MultiDocOPHHtml
+ --one_file_per_header
+ --src Source
+ --html
+ --multidoc
+ --documenttitle "ROBODoc API"
+ --index
+ --cmode
+ --tabsize 8
+ --nopre
+ --sections
+source items:
+ SYNOPSIS
+items:
+ SOURCE
+ NAME
+ COPYRIGHT
+ SYNOPSIS
+ USAGE
+ FUNCTION
+ DESCRIPTION
+ PURPOSE
+ AUTHOR
+ CREATION DATE
+ MODIFICATION HISTORY
+ HISTORY
+ INPUTS
+ INPUT
+ ARGUMENTS
+ OPTIONS
+ PARAMETERS
+ SWITCHES
+ OUTPUT
+ SIDE EFFECTS
+ RESULTS
+ RESULT
+ RETURN VALUE
+ EXAMPLE
+ NOTE
+ NOTES
+ DIAGNOSTICS
+ WARNINGS
+ ERRORS
+ BUGS
+ TODO
+ IDEAS
+ PORTABILITY
+ SEE ALSO
+ METHODS
+ NEW METHODS
+ ATTRIBUTES
+ NEW ATTRIBUTES
+ TAGS
+ COMMANDS
+ DERIVED FROM
+ DERIVED BY
+ USES
+ CHILDREN
+ USED BY
+ PARENTS
+ignore items:
+ HISTORY
+headertypes:
+ u Unittests robo_unittest
+ x "System tests" robo_sys_tests
+ e "Makefile entries" robo_mk_entries
+ignore files:
+ t
+ makefile.am
+ README
+ CVS
+ Doc
+ Doc1
+ Doc2
+ Doc3
+ FileFilterSrc
+ FileFilterSrc_2
+ RoboHeaders
+ Scr1
+ Scr2
+ SortSource
+ *.dat
+ *.html
+ *.rc
+ late*
+ troff*
+ sgml*
+ *.css
+ *.refs
+ *.in
+ *.txt
+ *~
+ *.stderr
+ Scr1
+ Scr2
+ Doc1
+ Doc2
+ SortSource
+ RoboHeaders
+ FileFilterSrc
+remark begin markers:
+ /*
+ #
+remark end markers:
+ */
+ #
+
--- /dev/null
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH robohdrs "1" "August 2003" "robohdrs 0.02"
+.SH NAME
+robohdrs \- insert ROBODoc headers to source code.
+.SH SYNOPSIS
+.B robohdrs
+.B [options]
+<source file>
+
+.SH DESCRIPTION
+
+robohdrs inserts ROBODoc headers to source code files. This program
+processes one source file at the time. Existing ROBODoc headers, if
+any, are not checked for. Beware since this may result in double
+headers. Current working directory should be the same as where the
+source file is located.
+
+By default NAME and SYNOPSIS items are included in the template
+header.
+
+.SH OPTIONS
+
+.IP -h
+Show brief help on usage and exit.
+.IP -i
+Specify an extra header item to be included in template header. Repeat
+if multiple extra items are desired.
+.IP -l
+Specify source code language (default C/C++) Supported options are:
+fortran, fortran90, script, and tex.
+.IP -p
+Specify project name for source file header.
+.IP -s
+Include SOURCE item.
+.IP -t
+Specify version control tag to be included in the main header.
+.IP -x
+Specify path to ctags binary which is used.
+
+.SH EXAMPLES
+
+.EX
+robohdrs -s -p myproj -i "MODIFICATION HISTORY" -i IDEAS test.c
+.EE
+
+Specifies project name myproj and inserts extra items MODIFICATION
+HISTORY, IDEAS, and SOURCE to all template headers (excluding source
+file header).
+
+.EX
+robohdrs -s -p myproj -l script test.tcl
+.EE
+
+Insert headers to a Tcl/Tk script.
+
+.SH COPYRIGHT
+
+Copyright \(co 2003 Frans Slothouber and Petteri Kettunen and Jacco van Weert.
+.br
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+.SH SEE ALSO
+
+.BR robodoc(1)
+
+
+
--- /dev/null
+<section>
+<title>Tips and Tricks</title>
+
+<section>
+<title>The SOURCE Item</title>
+
+ <para> With a little extra work you can include part of your
+ source code into your documentation too. The following example
+ shows how this is done:</para>
+
+<programlisting>
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ * RB_Panic - Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ * RB_Panic (cause, add_info)
+ * RB_Panic (char *, char *)
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+ void RB_Panic (char *cause, char *add_info)
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</programlisting>
+
+<para>You can add a SOURCE item as the last item of your header. Then
+ instead of closing your header with an end marker, you close it
+ normally. The end marker is instead placed at the end of the
+ fragment of source code that you want to include. </para>
+
+<para>SOURCE items are included by default. If you want to create a
+ document without the SOURCE items use the option
+ <option>--nosource</option>.</para>
+
+<para>You can also make normal items work like the source item,
+ see <xref linkend="sourceitems" endterm="sourceitems.title" />.
+</para>
+</section>
+
+
+<section>
+<title>Minimizing Duplicate Information</title>
+
+<para>It is always good to avoid having the same information in several different locations.
+ It is easy to create headers that have a lot information duplication. Take for instance
+ the following header.
+</para>
+
+<programlisting>
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ * RB_Panic - Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ * RB_Panic (cause, add_info)
+ * RB_Panic (char *, char *)
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+ void RB_Panic (char *cause, char *add_info)
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</programlisting>
+
+<para>
+ The name <literal>RB_Panic</literal> occurs five times. This is tedious to
+ type and difficult to maintain.
+ However with a the right <filename>robodoc.rc</filename> this can be changed
+ to:
+</para>
+
+<programlisting>
+/****f* Robodoc/RB_Panic [2.0d]
+ * SUMMARY
+ * Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ */
+
+void RB_Panic (char* cause, char *add_info)
+
+/*
+ * FUNCTION
+ * Prints an error message.
+ * Frees all resources used by robodoc.
+ * Terminates program.
+ * INPUTS
+ * cause - pointer to a string which describes the
+ * cause of the error.
+ * add_info - pointer to a string with additional information.
+ * SEE ALSO
+ * RB_Close_The_Shop ()
+ * SOURCE
+ */
+ {
+ printf ("Robodoc: Error, %s\n",cause) ;
+ printf (" %s\n", add_info) ;
+ printf ("Robodoc: Panic Fatal error, closing down..\n") ;
+ RB_Close_The_Shop () ; /* Free All Resources */
+ exit(100) ;
+ }
+
+/*******/
+</programlisting>
+
+<para><literal>RB_Panic</literal> occurs only twice now. In addition changes
+to the function definition only have to be done once.</para>
+
+<para>The <filename>robodoc.rc</filename> required for this is: </para>
+
+<programlisting>
+# robodoc.rc file
+items:
+ SUMMARY
+ SYNOPSIS
+ INPUTS
+ OUTPUTS
+ SEE ALSO
+ BUGS
+source items:
+ SYNOPSIS
+remark begin markers:
+ /*
+remark end markers:
+ */
+</programlisting>
+
+</section>
+
+<!-- TODO explain about headers with more than one name -->
+
+<section>
+<title>Advanced formatting with raw HTML and LaTeX code</title>
+
+ <para> By default an item's body shows up in your documentation in
+ the same way as it is formatted in your source code. All special
+ characters for the output mode are escaped. For instance an <
+ is translated to a &lt; in HTML mode. Sometimes however you
+ like to have some more control of what goes into the
+ documentation. This is possible with the piping. If a line of
+ your item's body starts with one of the special piping markers, the
+ text after this marker is copied verbatim into your documentation.
+ The following example shows how this is done, and how to add
+ equations to your documentation.
+ </para>
+
+<programlisting>
+/****m* pipe/pipetest
+ * NAME
+ * pipetest
+ * DESCRIPTION
+ * Simple header to show "piping" features in items.
+ * EXAMPLE
+ * Only "pipes" which match selected output style are picked up.
+ * |html <CENTER>This will be included in <B>HTML</B> output.</CENTER>
+ * |latex \centerline{This will be included in \LaTeX output}
+ * Space is mandatory following the pipe marker. The following is not a
+ * valid pipe marker:
+ * |html<B>Moi!</B>
+ * You should see an equation on the following line:
+ * |html y = x^2 (sorry, plain HTML is not very powerful)
+ * |latex \centerline{$y = x^2$}
+ * How does this look like?
+ * Here comes a multi-line equation array:
+ * |latex \begin{eqnarray}
+ * |latex \frac{\partial u}{\partial \tau} & = & D_u {\nabla}^2 u +
+ * |latex \frac{1}{\epsilon}
+ * |latex \left ( \hat{u}-{\hat{u}}^2-f\, {v} \, \frac{\hat{u}-q}{\hat{u}+q}
+ * |latex \right ) \; , \label{diffspot:1} \\
+ * |latex \frac{\partial v}{\partial \tau} & = & \hat{u}-v \; ,
+ * |latex \label{diffspot:2} \\
+ * |latex \frac{\partial r}{\partial \tau} & = & D_r {\nabla}^2 r \; .
+ * |latex \label{diffspAot:3}
+ * |latex \end{eqnarray}
+ * |html <I>TODO: write this in html</I>
+ * End of the example.
+ ******
+ */
+</programlisting>
+</section>
+
+<section>
+<title>Linking to external documents (href, file, mailto, images)</title>
+
+ <para> In HTML mode ROBODoc recognizes the following links to
+ external documents. </para>
+
+ <itemizedlist>
+
+ <listitem><para><literal>href:body</literal> -- This is replaced with
+ <literal><a href="body">body</A></literal>
+ </para></listitem>
+
+ <listitem><para><literal>file:/body</literal> -- This is replaced with
+ <literal><a href="file:/body">file:/body</A></literal>
+ </para></listitem>
+
+ <listitem><para><literal>mailto:body</literal> -- This is replaced with
+ <literal><a href="mailto:body">body</A></literal>
+ </para></listitem>
+
+ <listitem><para><literal>http://body</literal> -- This is replaced with
+ <literal><a href="http://body">http://body</A></literal>
+ </para></listitem>
+
+ <listitem><para><literal>image:body</literal> -- This is replaced with
+ <literal><image src="body"></literal>
+ </para></listitem>
+
+ </itemizedlist>
+</section>
+
+
+<section>
+<title>Linking from an external document</title>
+
+ <para>To link from an external document to one of the HTML
+ documents generated by ROBODoc you need a label. ROBODoc creates
+ two labels for each header. The first one starts with
+ <literal>robo</literal> followed by a number. You can not use
+ this one because the numbers will change each time you run
+ ROBODoc. The second label is an escaped version of the whole
+ header name. In this label all the non alphanumeric characters of
+ the name are replaced by their two digit hexadecimal code.</para>
+
+ <para>An example, if your header name is
+ <literal>Analyser/RB_ToBeAdded</literal> the label is
+ <literal>Analyser2fRB5fToBeAdded</literal>. Here
+ <literal>/</literal> was replaced by <literal>2f</literal> and
+ <literal>_</literal> was replaced by <literal>5f</literal>. As
+ long as you do not change the header name, this label stays the
+ same each time you run ROBODoc.</para>
+
+</section>
+
+<section id="tools">
+<title id="tools.title">Using external tools</title>
+ <para>
+ You can also execute external tools from <literal>ROBODoc</literal> and even
+ pass data to them.
+ The output of these tools can be included in your documentation for instance.
+ </para>
+ <para>
+ There are several types of external tools you can use:
+ </para>
+ <orderedlist>
+ <listitem>Arbitrary tool with passing data through stdin</listitem>
+ <listitem>Arbitrary tool without passing data through stdin</listitem>
+ <listitem>The <literal>DOT</literal> tool</listitem>
+ </orderedlist>
+ <para>
+ The following example shows how to use each of them.
+ </para>
+
+<programlisting>
+/****m* tools/tooltest
+ *
+ * NAME
+ * Tooltest
+ *
+ * DESCRIPTION
+ * Example showing how to invoke external tools.
+ *
+ * EXAMPLE
+ * This one sorts elements into the file href:nexus-6.txt
+ * The input data is passed through stdin.
+ *
+ * |tool sort > nexus-6.txt
+ * Zhora
+ * Pris
+ * Leon Kowalski
+ * Roy Batty
+ * Rachel
+ * Rick Deckard?
+ * |tool end
+ *
+ * We can also execute tools without having any stdin data.
+ * In the following example the output is simply redirected into href:tears.txt
+ *
+ * |exec echo "All those moments will be lost in time like tears in rain." > tears.txt
+ *
+ * You can also include neat DOT graphs in your documentation.
+ * This one shows a hash table.
+ *
+ * |dot start
+ * digraph G {
+ * nodesep=.05;
+ * rankdir=LR;
+ * node [shape=record,width=.1,height=.1];
+ *
+ * node0 [label = "<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> | ",height=2.0];
+ * node [width = 1.5];
+ * node1 [label = "{<n> n14 | 719 |<p> }"];
+ * node2 [label = "{<n> a1 | 805 |<p> }"];
+ * node3 [label = "{<n> i9 | 718 |<p> }"];
+ * node4 [label = "{<n> e5 | 989 |<p> }"];
+ * node5 [label = "{<n> t20 | 959 |<p> }"] ;
+ * node6 [label = "{<n> o15 | 794 |<p> }"] ;
+ * node7 [label = "{<n> s19 | 659 |<p> }"] ;
+ *
+ * node0:f0 -> node1:n;
+ * node0:f1 -> node2:n;
+ * node0:f2 -> node3:n;
+ * node0:f5 -> node4:n;
+ * node0:f6 -> node5:n;
+ * node2:p -> node6:n;
+ * node4:p -> node7:n;
+ * }
+ * |dot end
+ *
+ ******
+ */
+</programlisting>
+
+ <para>
+ If you want to use the <literal>DOT</literal> tool you need the
+ <literal>Graphviz</literal> package.
+ More information and the binaries can be found at
+ <ulink url="http://www.graphviz.org/">
+ <citetitle>http://www.graphviz.org/
+ </citetitle></ulink>.
+ The created graphs are automatically included in the documentation
+ (<literal>HTML</literal> and <literal>LaTeX</literal> only).
+ If you generate <literal>PDF</literal> output from your <literal>LaTeX</literal>
+ file and you want to include <literal>DOT</literal> graphs in it, you will also
+ need the <command>epstopdf</command> utility.
+ <literal>ROBODoc</literal> lets <literal>DOT</literal> generate
+ <literal>PNG</literal> images for <literal>HTML</literal> output and
+ <literal>PS</literal> and <literal>PDF</literal> images for
+ <literal>LaTeX</literal> output.
+ </para>
+</section>
+
+<section>
+<title>ROBODoc-ing an existing project</title>
+
+ <para>
+ The ROBODoc package includes also a standalone binary named
+ <literal>robohdrs</literal>.
+ This helper program can take clean source file and insert
+ ROBODoc headers to functions, global variables, and macros.
+ There are issues with this tool but it saves lots of cumbersome typing
+ when starting on documenting an existing code-base with ROBODoc.
+ Type
+<command>
+man robohdrs
+</command>
+ or
+<command>
+robohdrs -h
+</command>
+ for help.
+ Example:
+<command>
+robohdrs -s -p testproj -i "MODIFICATION HISTORY" -i IDEAS testproj.c
+</command>
+</para>
+
+ <para>
+ Note that <literal>robohdrs</literal> is supported on UNIX-like platforms only.
+ It requires <literal>fork()</literal> and Exuberant Ctags 5.3.1 or newer.
+ </para>
+
+</section>
+
+
+<section>
+<title>Using ROBODoc under Windows</title>
+ <para>When you use ROBODoc under windows, don't forget that it is
+ a command line tool. ROBODoc relies on the console window to
+ inform you about problems and errors.</para>
+
+ <para>An easy mistake to make is to create a shortcut to
+ <literal>robodoc.exe</literal> and then click on the icon to
+ generate the documentation each time you made some changes to your
+ source code. If you have a fast machine a console window pops up
+ quickly and after that your documentation is ready.</para>
+
+ <para>This works very fine until you make a mistake in one of your
+ headers. The console window still pops up, but before you have a chance
+ to read any of the error messages it is gone again. Most likely
+ you won't even have noticed there were error messages. You will
+ end up with empty documentation or old documentation. </para>
+
+ <para>It is Better to create a batch file with the following commands
+ and to store all the options in a <filename>robodoc.rc</filename>
+ file:</para>
+<programlisting>
+robodoc.exe
+pause
+</programlisting>
+ <para>Now the console window stays open and you have the
+ opportunity to read the error messages.</para>
+
+ <para>While the window is open, right click on the title bar,
+ go to properties->layout and set the buffer size to something
+ like 2500. That way, the next time you run it, you can scroll back
+ and view all error messages.
+ </para>
+
+</section>
+
+</section>
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+<head>
+<link rel="stylesheet" href="./robodoc.css" type="text/css">
+<title>Classes</title>
+<!-- Source: ./Source/ -->
+<!-- Generated with ROBODoc Version 4.0.17 (Dec 29 2003) -->
+<!-- ROBODoc (c) 1994-2003 by Frans Slothouber and Jacco van Weert. -->
+</head>
+<body bgcolor="#FFFFFF">
+<a name="./Source/">Generated from ./Source/</a> with <a href="http://sourceforge.net/projects/robodoc/">ROBODoc</a> v4.0.17 on Mon Dec 29 20:09:37 2003
+<br />
+<p>
+[<a href="./robo_sourcefiles.html#top">Sourcefiles</a>]
+[<a href="./masterindex.html#top">Index</a>]
+[<a href="#top">Classes</a>]
+[<a href="./robo_modules.html#top">Modules</a>]
+[<a href="./robo_methods.html#top">Methods</a>]
+</p>
+<h1>Classes</h1>
+<table cellspacing="3">
+<tr class="even">
+<td class="uneven"><a href="./Box_pm.html#robo5"><tt>
+Box</tt></a></td>
+<td class="even"><a href="./Box/RectangularBox_pm.html#robo9"><tt>
+RectangularBox</tt></a></td>
+<td class="uneven"><a href="./SmartLoader_pm.html#robo1"><tt>
+SmartLoader</tt></a></td>
+<td class="even"><a href="./Box/SquareBox_pm.html#robo6"><tt>
+SquareBox</tt></a></td>
+</tr>
+<tr class="uneven">
+<td></td>
+<td></td>
+<td></td>
+<td></td>
+</tr>
+</table>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+<head>
+<link rel="stylesheet" href="./robodoc.css" type="text/css">
+<title>Methods</title>
+<!-- Source: ./Source/ -->
+<!-- Generated with ROBODoc Version 4.0.17 (Dec 29 2003) -->
+<!-- ROBODoc (c) 1994-2003 by Frans Slothouber and Jacco van Weert. -->
+</head>
+<body bgcolor="#FFFFFF">
+<a name="./Source/">Generated from ./Source/</a> with <a href="http://sourceforge.net/projects/robodoc/">ROBODoc</a> v4.0.17 on Mon Dec 29 20:09:37 2003
+<br />
+<p>
+[<a href="./robo_sourcefiles.html#top">Sourcefiles</a>]
+[<a href="./masterindex.html#top">Index</a>]
+[<a href="./robo_classes.html#top">Classes</a>]
+[<a href="./robo_modules.html#top">Modules</a>]
+[<a href="#top">Methods</a>]
+</p>
+<h1>Methods</h1>
+<table cellspacing="3">
+<tr class="even">
+<td class="uneven"><a href="./Box/RectangularBox_pm.html#robo10"><tt>
+RectangularBox::volume</tt></a></td>
+<td class="even"><a href="./SmartLoader_pm.html#robo2"><tt>
+SmartLoader::pack</tt></a></td>
+<td class="uneven"><a href="./Box/SquareBox_pm.html#robo7"><tt>
+SquareBox::side</tt></a></td>
+<td class="even"><a href="./Box/SquareBox_pm.html#robo8"><tt>
+SquareBox::volume</tt></a></td>
+</tr>
+<tr class="uneven">
+<td></td>
+<td></td>
+<td></td>
+<td></td>
+</tr>
+</table>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+<head>
+<link rel="stylesheet" href="./robodoc.css" type="text/css">
+<title>Modules</title>
+<!-- Source: ./Source/ -->
+<!-- Generated with ROBODoc Version 4.0.17 (Dec 29 2003) -->
+<!-- ROBODoc (c) 1994-2003 by Frans Slothouber and Jacco van Weert. -->
+</head>
+<body bgcolor="#FFFFFF">
+<a name="./Source/">Generated from ./Source/</a> with <a href="http://sourceforge.net/projects/robodoc/">ROBODoc</a> v4.0.17 on Mon Dec 29 20:09:37 2003
+<br />
+<p>
+[<a href="./robo_sourcefiles.html#top">Sourcefiles</a>]
+[<a href="./masterindex.html#top">Index</a>]
+[<a href="./robo_classes.html#top">Classes</a>]
+[<a href="#top">Modules</a>]
+[<a href="./robo_methods.html#top">Methods</a>]
+</p>
+<h1>Modules</h1>
+<table cellspacing="3">
+<tr class="even">
+<td class="uneven"><a href="./Cargo_txt.html#robo4"><tt>
+Cargo</tt></a></td>
+<td class="even"><a href="./Loader_txt.html#robo3"><tt>
+Loader</tt></a></td>
+<td class="uneven"><a href="./TruckPacker_pl.html#robo0"><tt>
+TruckPacker</tt></a></td>
+</tr>
+<tr class="uneven">
+<td></td>
+<td></td>
+<td></td>
+</tr>
+</table>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+<head>
+<link rel="stylesheet" href="./robodoc.css" type="text/css">
+<title>Sourcefiles</title>
+<!-- Source: ./Source/ -->
+<!-- Generated with ROBODoc Version 4.0.17 (Dec 29 2003) -->
+<!-- ROBODoc (c) 1994-2003 by Frans Slothouber and Jacco van Weert. -->
+</head>
+<body bgcolor="#FFFFFF">
+<a name="./Source/">Generated from ./Source/</a> with <a href="http://sourceforge.net/projects/robodoc/">ROBODoc</a> v4.0.17 on Mon Dec 29 20:09:37 2003
+<br />
+<p>
+[<a href="#top">Sourcefiles</a>]
+[<a href="./masterindex.html#top">Index</a>]
+[<a href="./robo_classes.html#top">Classes</a>]
+[<a href="./robo_modules.html#top">Modules</a>]
+[<a href="./robo_methods.html#top">Methods</a>]
+</p>
+<ul>
+<li>
+./Source/<ul>
+<li>
+<a href="./Box_pm.html#Box.pm"><tt>
+Box.pm</tt></a></li>
+<li>
+<a href="./Cargo_txt.html#Cargo.txt"><tt>
+Cargo.txt</tt></a></li>
+<li>
+<a href="./Loader_txt.html#Loader.txt"><tt>
+Loader.txt</tt></a></li>
+<li>
+<a href="./SmartLoader_pm.html#SmartLoader.pm"><tt>
+SmartLoader.pm</tt></a></li>
+<li>
+<a href="./TruckPacker_pl.html#TruckPacker.pl"><tt>
+TruckPacker.pl</tt></a></li>
+<li>
+./Source/Box/<ul>
+<li>
+<a href="./Box/RectangularBox_pm.html#RectangularBox.pm"><tt>
+RectangularBox.pm</tt></a></li>
+<li>
+<a href="./Box/SquareBox_pm.html#SquareBox.pm"><tt>
+SquareBox.pm</tt></a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</body>
+</html>
--- /dev/null
+#!/usr/bin/perl -w
+
+#****c* Cargo/Box
+# FUNCTION
+# A box that can be packed into truck.
+# Several other classes are derived from Box.
+# Box
+# |
+# +---- SquareBox
+# |
+# +---- RectangularBox
+#******
+
+package Box;
+
+sub new {
+ my $class = shift;
+ my $self = { };
+ bless ($self, $class);
+ return $self;
+}
+
+1;
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#****c* Box/RectangularBox
+# FUNCTION
+# A box with the property that are sides are equal.
+# ATTRIBUTES
+# DEPTH -- the depth of the box.
+# HEIGHT -- the height of the box.
+# WIDTH -- the width of the box.
+# DERIVED FROM
+# Box
+#******
+
+package RectangularBox;
+
+use Box;
+use vars ('@ISA');
+@ISA = ("Box");
+
+sub new {
+ my $classname = shift;
+ my $self = $classname->SUPER::new(@_);
+ $self->{DEPTH} = 1;
+ $self->{HEIGHT} = 1;
+ $self->{WIDTH} = 1;
+ return $self;
+}
+
+#****m* Box/RectangularBox::volume
+# FUNCTION
+# Compute the volume of the rectangular box.
+# SYNOPSIS
+# my $volume = $boxref->volume();
+# RETURN VALUE
+# The volume of the box
+# SOURCE
+
+sub volume {
+ my $self = { };
+ return $self->{DEPTH} * $self->{HEIGHT} * $self->{WIDTH};
+}
+
+#*****
+
+1;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#****c* Box/SquareBox
+# FUNCTION
+# A box with the property that are sides are equal.
+# ATTRIBUTES
+# SIDE_LENGTH -- the length of each side
+# DERIVED FROM
+# Box
+# SOURCE
+
+package SquareBox;
+
+use Box;
+use vars ('@ISA');
+@ISA = ("Box");
+
+sub new {
+ my $classname = shift;
+ my $self = $classname->SUPER::new(@_);
+ $self->{SIDE} = 1;
+ return $self;
+}
+
+#*******
+
+
+#****m* Box/SquareBox::side
+# FUNCTION
+# Set or get the side length of the square box.
+# SYNOPSIS
+# $boxref->side(100.25);
+# my $length = $boxref->side();
+# RETURN VALUE
+# The volume of the box
+# SOURCE
+
+sub side {
+ my $self = shift;
+ if (@_) {
+ my $length = shift;
+ $self->{SIDE} = $length;
+ }
+ return $self->{SIDE};
+}
+
+#*******
+
+#****m* Box/SquareBox::volume
+# FUNCTION
+# Compute the volume of a square box.
+# SYNOPSIS
+# my $volume = $boxref->volume();
+# RETURN VALUE
+# The volume of the box
+# SOURCE
+
+sub volume {
+ my $self = { };
+ return $self{SIDE} * $self{SIDE} * $self{SIDE} ;
+}
+
+#*****
+1;
+
--- /dev/null
+
+#****h* TruckPacker/Cargo
+# FUNCTION
+#
+# The Cargo module consist of a number of classes that represent
+# that cargo that can be loaded into a truck.
+# The most important one is Box.
+#
+#****
--- /dev/null
+#****h* TruckPacker/Loader
+# FUNCTION
+# The Loader module consist of a number of classes that represent
+# that various algorithms to pack Cargo into a truck..
+#
+# Currently only on algorithm (SmartLoader) has been implemented.
+#
+#****
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#****c* Loader/SmartLoader
+# FUNCTION
+# This class implements an O(1) packing
+# algorithm.
+# ATTRIBUTES
+# CARGO -- the cargo to be packed.
+#******
+
+Package SmartLoader;
+
+sub new {
+ my $class = shift;
+ my $self = { };
+ bless ($self, $class);
+ my $self->{CARGO} = ();
+ return $self;
+}
+
+#****m* Loader/SmartLoader::pack
+# FUNCTION
+# A O(1) packing algorithm.
+# SYNOPSIS
+# my $sequence = $packref->pack();
+# RETURN VALUE
+# A squence that specifies how to pack the truck.
+#******
+
+sub pack {
+ return 0;
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl -w
+
+#****h* FactoryTools/TruckPacker
+# FUNCTION
+# A program to effectively pack a truck with cargo.
+# It consists of two modules:
+# Cargo -- classes to define the kind of cargo
+# that can be packed.
+# Loader -- classes to define various packing
+# algorithms.
+#
+#****
+
+use Box;
+use SquareBox;
+use RectangularBox;
+use Loader;
+
+sub main {
+ print "Hello world\n";
+}
+
+main();
+
+
--- /dev/null
+options:
+ --src ./Source
+ --doc ./Doc
+ --html
+ --multidoc
+ --sections
+ --tell
+ --toc
+ --index
+ignore files:
+ CVS
--- /dev/null
+****h* main_module/module [1.0] *
+*
+* NAME
+*
+* COPYRIGHT
+*
+* FUNCTION
+*
+* AUTHOR
+*
+* CREATION DATE
+*
+* MODIFICATION HISTORY
+*
+* NOTES
+*
+*******
+
+
+****f* module/procname [1.0] *
+*
+* NAME
+*
+* SYNOPSIS
+*
+* FUNCTION
+*
+* INPUTS
+*
+* RESULT
+*
+* EXAMPLE
+*
+* NOTES
+*
+* BUGS
+*
+* SEE ALSO
+*
+**********
+
+
+****i* module/i_procname [1.0] *
+*
+* NAME
+*
+* SYNOPSIS
+*
+* FUNCTION
+*
+* INPUTS
+*
+* RESULT
+*
+* EXAMPLE
+*
+* NOTES
+*
+* BUGS
+*
+* SEE ALSO
+*
+**********
--- /dev/null
+REM ****h* main_module/module [1.0] *
+REM
+REM NAME
+REM
+REM COPYRIGHT
+REM
+REM FUNCTION
+REM
+REM AUTHOR
+REM
+REM CREATION DATE
+REM
+REM MODIFICATION HISTORY
+REM
+REM NOTES
+REM
+REM *****
+
+
+REM ***** module/procname [1.0] *
+REM
+REM NAME
+REM
+REM SYNOPSIS
+REM
+REM FUNCTION
+REM
+REM INPUTS
+REM
+REM RESULT
+REM
+REM EXAMPLE
+REM
+REM NOTES
+REM
+REM BUGS
+REM
+REM SEE ALSO
+REM
+REM ********
+
+
+REM ****i* module/i_procname [1.0] *
+REM
+REM NAME
+REM
+REM SYNOPSIS
+REM
+REM FUNCTION
+REM
+REM INPUTS
+REM
+REM RESULT
+REM
+REM EXAMPLE
+REM
+REM NOTES
+REM
+REM BUGS
+REM
+REM SEE ALSO
+REM
+REM ********
--- /dev/null
+/****h* projectname/module_name [1.0]
+* NAME
+* COPYRIGHT
+* FUNCTION
+* AUTHOR
+* CREATION DATE
+* MODIFICATION HISTORY
+* NOTES
+*******
+*/
+
+
+/****f* module_name/function_name [1.0] *
+* NAME
+* SYNOPSIS
+* FUNCTION
+* INPUTS
+* RESULT
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
+
+/****s* module_name/stucture_name [1.0] *
+* NAME
+* PURPOSE
+* ATTRIBUTES
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
+
+/****v* module_name/variable_name [1.0] *
+* NAME
+* PURPOSE
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
--- /dev/null
+/****h* projectname/module_name [1.0]
+* NAME
+* COPYRIGHT
+* FUNCTION
+* AUTHOR
+* CREATION DATE
+* MODIFICATION HISTORY
+* NOTES
+*******
+*/
+
+
+/****f* module_name/funtion_name [1.0] *
+* NAME
+* SYNOPSIS
+* FUNCTION
+* INPUTS
+* RESULT
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
+/****f* module_name/funtion_name2 [1.0] *
+* NAME
+* SYNOPSIS
+* FUNCTION
+* INPUTS
+* RESULT
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+* SOURCE
+*/
+
+example()
+
+/**********/
+
+
+/****c* module_name/class_name [1.0] *
+* NAME
+* PURPOSE
+* METHODS
+* DERIVED FROM
+* DERIVED BY
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
+
+/****m* module_name/method_name [1.0] *
+* NAME
+* SYNOPSIS
+* PURPOSE
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+**********
+*/
+
--- /dev/null
+\ ****h* main_module/module [1.0] *
+\ NAME
+\ module
+\ COPYRIGHT
+\ moi copyright
+\ FUNCTION
+\ moi function
+\ AUTHOR
+\ moi author
+\ CREATION DATE
+\ moi date
+\ MODIFICATION HISTORY
+\ moi history
+\ NOTES
+\ *******
+
+\ ****f* module/procname [1.0] *
+\ NAME
+\ SYNOPSIS
+\ FUNCTION
+\ INPUTS
+\ RESULT
+\ EXAMPLE
+\ NOTES
+\ BUGS
+\ SEE ALSO
+\ ********
+
+\ ****f* module/procname2 [1.0] *
+\ NAME
+\ SYNOPSIS
+\ FUNCTION
+\ INPUTS
+\ RESULT
+\ EXAMPLE
+\ NOTES
+\ BUGS
+\ SEE ALSO
+\ SOURCE
+
+hello_forth()
+
+\ ********
--- /dev/null
+C ****h* main_module/module [1.0] *
+C
+C NAME
+C
+C COPYRIGHT
+C
+C FUNCTION
+C
+C AUTHOR
+C
+C CREATION DATE
+C
+C MODIFICATION HISTORY
+C
+C NOTES
+C
+C *****
+
+
+C ***** module/procname [1.0] *
+C
+C NAME
+C
+C SYNOPSIS
+C
+C FUNCTION
+C
+C INPUTS
+C
+C RESULT
+C
+C EXAMPLE
+C
+C NOTES
+C
+C BUGS
+C
+C SEE ALSO
+C
+C ********
+
+
+C ****i* module/i_procname [1.0] *
+C
+C NAME
+C
+C SYNOPSIS
+C
+C FUNCTION
+C
+C INPUTS
+C
+C RESULT
+C
+C EXAMPLE
+C
+C NOTES
+C
+C BUGS
+C
+C SEE ALSO
+C
+C ********
--- /dev/null
+<!---****h* projectname/module_name [1.0]
+* NAME
+* COPYRIGHT
+* FUNCTION
+* AUTHOR
+* CREATION DATE
+* MODIFICATION HISTORY
+* NOTES
+******* --->
+
+
+<!---****f* module_name/function_name [1.0] *
+* NAME
+* SYNOPSIS
+* FUNCTION
+* INPUTS
+* RESULT
+* EXAMPLE
+* NOTES
+* BUGS
+* SEE ALSO
+********** --->
+
+<!---****f* module_name/function_name2 [1.0] *
+* NAME
+* SYNOPSIS
+* FUNCTION
+* INPUTS
+* RESULT
+* EXAMPLE
+* NOTES
+* BUGS
+* SOURCE
+* --->
+
+<H1> Example Source </H1>
+
+<!---********---->
--- /dev/null
+clean:
+ rm -f *~
--- /dev/null
+#****h* main_module/module [1.0] *
+# NAME
+# COPYRIGHT
+# FUNCTION
+# AUTHOR
+# CREATION DATE
+# MODIFICATION HISTORY
+# NOTES
+#******
+#
+
+#****f* module/procname [1.0] *
+# NAME
+# SYNOPSIS
+# FUNCTION
+# INPUTS
+# RESULT
+# EXAMPLE
+# NOTES
+# BUGS
+# SEE ALSO
+#********
+#
+
+#****f* module/procname2 [1.0] *
+# NAME
+# SYNOPSIS
+# FUNCTION
+# INPUTS
+# RESULT
+# EXAMPLE
+# NOTES
+# BUGS
+# SEE ALSO
+# SOURCE
+
+example()
+
+#********
--- /dev/null
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ 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 only 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. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. 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.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. 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.
+
+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=c89 CFLAGS=-O2 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 must use a version of `make' that
+supports the `VPATH' variable, such as 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 `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have 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.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will 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.
+
+ 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'.
+
+Optional Features
+=================
+
+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.
+
+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 `--target=TYPE' option 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
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--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.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
--- /dev/null
+Debian users can install ROBODoc using apt-get as follows:
+
+ apt-get install robodoc
+
--- /dev/null
+See the changelog for a detailed list of add new
+features.
+
--- /dev/null
+$d: README,v 1.5 2003/01/16 06:26:43 gumpu Exp $
+
+ROBODoc Version 4.99.36 Jul 2007.
+
+ROBODoc is program documentation tool. The idea is to include for
+every function or procedure a standard header containing all
+sorts of information about the procedure or function. ROBODoc
+extracts these headers from the source file and puts them in a
+separate autodocs-file. ROBODoc thus allows you to include the
+program documentation in the source code and avoid having to
+maintain two separate documents. Or as Petteri puts it: "robodoc
+is very useful - especially for programmers who don't like
+writing documents with Word or some other strange tool."
+
+ROBODoc can format the headers in a number of different formats:
+HTML, RTF, LaTeX, or XML DocBook. In HTML mode it can
+generate cross links between headers. You can even include parts
+of your source code.
+
+ROBODoc works with many programming languages: For instance C,
+Pascal, Shell Scripts, Assembler, COBOL, Occam, Postscript,
+Forth, Tcl/Tk, C++, Java -- basically any program in which you
+can use remarks/comments.
+
+ o For information on how to build and install see INSTALL
+ o For information on how to use ROBODoc see Docs/manual.html.
+ o Blank headers for various languages can be found in Headers/
+ o For an example on how ROBODoc can be used see
+ the ROBODoc source code in Source/
+ o For licence information see COPYING
+ o For a change log see Source/robodoc.c
+
+Many people contributed to ROBODoc, to name a few:
+
+o Petteri Kettunen <petterik -a-t- iki.fi>
+ Bug fixes, FOLD, C features.
+o Bernd Koesling <KOESSI -a-t- CHESSY.aworld.de>
+ Bug fixes, functional improvements, code cleanup.
+o Anthon Pang <apang -a-t- mindlink.net>
+ RTF support, Bug fixes.
+o Thomas Aglassinger <agi -a-t- sbox.tu-graz.ac.at>
+ Fixes and cleanup of HTML-output
+o Stefan Kost <kost -a-t- imn.htwk-leipzig.de>
+ Idea of the master index file and different header types.
+
+Questions, found a bug or a typo; send an email to [rfsber -a-t- xs4all.nl]
+
+(c) 1994-2005 Frans Slothouber, Petteri Kettunen, and Jacco van Weert
+
+
+
--- /dev/null
+-nut
+-i 4
+-bli 0
+-bls
+-bad
+-npcs
+-bfda
+-di20
+-prs
+-npsl
+--verbose
+-T FILE
+-T actions_t
--- /dev/null
+For testing
--- /dev/null
+#****f* Test/Test1
+# DESCRIPTION
+# for testing
+#*****
+
--- /dev/null
+#****f* Test/Test2
+# DESCRIPTION
+# for testing
+#*****
+
--- /dev/null
+#****f* Test/Test3
+# DESCRIPTION
+# for testing
+#*****
+
--- /dev/null
+#****f* Test/Test4
+# DESCRIPTION
+# for testing
+#*****
+
--- /dev/null
+#****f* Accept/TT3
+# NAME
+# TT3
+#****
+
--- /dev/null
+#****f* Accept/TT
+# NAME
+# TT
+#****
+
--- /dev/null
+#****f* Accept/TT2
+# NAME
+# TT2
+#****
+
--- /dev/null
+#****f* Ignore/Ignore
+# Name
+# Ignore
+#***
--- /dev/null
+#****f* Ignore/Ignore
+# Name
+# Ignore
+#***
--- /dev/null
+use strict;
+package ROBOTest;
+
+my $ok = 1;
+my $name = "unknown";
+
+sub start {
+ $name = shift;
+# printf("%-50s", $name);
+}
+
+sub assertNotDir {
+ my $filename = shift;
+ assert( !( -d $filename ), "Directory $filename does not exists");
+}
+
+sub assertDir {
+ my $filename = shift;
+ assert( -d $filename, "Directory $filename exists");
+}
+
+sub assertNotFile {
+ my $filename = shift;
+ assert( !( -f $filename ), "File $filename does not exists");
+}
+
+sub assertFile {
+ my $filename = shift;
+ assert( -f $filename, "File $filename exists");
+}
+
+sub assert {
+ my $arg_ok = shift;
+ my $title = shift;
+ printf("%-32s", $name);
+ if ($title) {
+ printf("%-40s", $title);
+ } else {
+ printf("%-40s", "--");
+ }
+ if ($arg_ok) {
+ print " OK\n";
+ } else {
+ print " FAIL\n";
+ }
+}
+
+sub finish {
+# if ($ok) {
+# print "OK\n";
+# } else {
+# print "FAIL\n";
+# }
+}
+
+1;
+
--- /dev/null
+
+
+int an_int_var = 100;
+
+
+int test_function( int x, int y )
+{
+ int i;
+ for ( i = 0; i < 100; ++i )
+ {
+ printf("Hello\n");
+ }
+}
+
+int test_function2( int x, int y )
+{
+
+
+}
+
--- /dev/null
+#****h* Bbb__level2__/Dbb__level3__
+# FUNCTION
+# Test. Level 3.
+#******
+
+#****v* Dbb__level3/__var1__
+# FUNCTION
+# Test.
+#****
--- /dev/null
+
+#****h* Aaa__level0__/Aaa__level1__
+# FUNCTION
+# Test. This has name Aaa.. so it should
+# be a the begin of the TOC
+#******
+
+#****h* Aaa__level1__/Bbb__level2__
+# FUNCTION
+# Test. Level 2.
+#******
+
+#****h* Bbb__level2__/Abb__level3__
+# FUNCTION
+# Test. Level 3.
+#******
+
+#****h* Bbb__level2__/Bbb__level3__
+# FUNCTION
+# Test. Level 3.
+#******
+
+#****h* Bbb__level2__/Cbb__level3__
+# FUNCTION
+# Test. Level 3.
+#******
+
--- /dev/null
+
+#****d* Test/_test_def1_test_
+# NAME
+#
+#****
+
+#****d* Test/_test_def2_test_
+# NAME
+#
+#****
+
+
+#****f* Test/_test_fun1_test_
+# NAME
+#
+#****
+
+#****f* Test/_test_fun2_test_
+# NAME
+#
+#****
+
+#****c* Test/_test_class1_test_
+# NAME
+#
+#****
+
+#****c* Test/_test_class2_test_
+# NAME
+#
+#****
+
+#****c* Test/_test_class3_test_
+# NAME
+#
+#****
+
+
+#****v* Test/_test_var1_test_
+# NAME
+#
+#*****
+
+#****h* Test/_test_mod1_test_
+# NAME
+#
+#*******
+
+#****s* Test/_test_struct1_test_
+# NAME
+#
+#******
+
+
+#****t* Test/_test_type1_test_
+# NAME
+#
+#******
+
+#****e* Test/_test_exception1_test_
+# NAME
+#
+#******
+
+
+#****p* Test/_test_procedure1_test_
+# NAME
+#
+#******
+
+
--- /dev/null
+/****if* Test/Internal1
+ * NAME
+ * Internal1
+ *
+ *****
+ */
+
+/****f* Test/Public1
+ * NAME
+ * Public1
+ *
+ *****
+ */
+
+/****iv* Test/Internal_Var1
+ * NAME
+ * Internal1
+ *
+ *****
+ */
+
+
+
--- /dev/null
+#****h* AA_Header/BB_header
+# NAME
+# BB_Header --
+#****
+
+#****f* BB_header/aaa_function
+# NAME
+# aaa_function --
+#****
+
+#****f* BB_header/bbb_function
+# NAME
+# bbb_function --
+#****
+
+#****f* BB_header/ccc_function
+# NAME
+# ccc_function --
+#****
+
+
--- /dev/null
+#****h* XX_Header/ZZ_Header
+# NAME
+# ZZ_Header --
+#****
+
+#****h* XX_Header/AA_Header
+# NAME
+# AA_Header --
+#****
+
+#****h* ZZ_Header/DD_Header
+# NAME
+# DD_Header --
+#****
+
+#****h* AA_Header/CC_header
+# NAME
+# CC_Header --
+#****
+
+#****f* DD_header/aaa_function
+# NAME
+# bbb_function --
+#****
+
+#****f* CC_header/ccc_function
+# NAME
+# ccc_function --
+#****
+
+#****f* CC_header/aaa_function
+# NAME
+# aaa_function --
+#****
+
+#****f* CC_header/fff_function
+# NAME
+# fff_function --
+#****
+
+#****f* CC_header/eee_function
+# NAME
+# eee_function --
+#****
+
+
--- /dev/null
+options:
+ --src
+ ./option_rc_test.dat
+ --doc
+ ./option_rc_test
+ --singlefile
--- /dev/null
+/* C_MODE test file */
+/* $Id: cmode_test.dat,v 1.3 2002/04/06 06:25:29 petterik Exp $ */
+/****f* Test/cmode
+* NAME
+* C_MODE test file
+* SOURCE
+*/
+
+void tes(void)
+{
+ char quote = 0; /* TEST_COMMENT */
+ printf("'"); /* TEST_COMMENT */
+ ch = '\''; /* TEST_COMMENT */
+ ch = '\\'; /* TEST_COMMENT */
+ char *str1 = "\\huuhaa"; /* TEST_COMMENT */
+ char *str2 = "'''"; /* TEST_COMMENT */
+ printf("/* huuhaa */" "\\" "huuhaa"); /* TEST_COMMENT */
+ /* unbalanced double quotes in comments "foobar"" */
+ /* TEST_COMMENT */
+ /* unbalanced single quotes in comments ' */
+ /* TEST_COMMENT */
+ strncmp("/*", cur_char, 2); /* TEST_COMMENT */
+ fprintf(dest_doc, "<HEINEKEN COLOR = \"#00FF00\">/*"); /* TEST_COMMENT */
+ strncmp("*/", cur_char, 2); /* TEST_COMMENT */
+ fprintf(dest_doc, "*/</HEINEKEN>"); /* TEST_COMMENT */
+}
+
+/**********/
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/cmode_test1_pl
+# FUNCTION
+# Tests the option --cmode. With this option
+# comments in C sourcecode should be colored.
+# We test this by looking for the <FONT> tag.
+# SOURCE
+#
+
+ROBOTest::start("C-mode Test");
+
+my $ok = 1;
+while (my $line = <>) {
+ if ($line =~ /TEST_COMMENT/) {
+ my @match = ($line =~ m/FONT/ig);
+ my $cnt = scalar(@match);
+ if ($cnt != 2) {
+ $ok = 0;
+ last;
+ }
+ }
+}
+
+ROBOTest::assert($ok);
+ROBOTest::finish();
+
+0;
+
+#*****
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: doc_dir_filter_test.pl,v 1.1 2004/06/20 09:24:36 gumpu Exp $
+#
+
+#****x* SystemTest/doc_dir_filter_test_pl
+# FUNCTION
+# If a user specifies a doc dir that is a subdir of the source
+# dir that robodoc has to skip it when scanning the sources.
+# We test this by running robodoc twice with the options
+# --src .
+# --doc ./NotDoc
+# (These are specified in doc_dir_filter_test.rc
+# SOURCE
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Doc Dir Filter Test");
+ROBOTest::assertNotDir("DocNot/DocNot");
+ROBOTest::finish;
+
+#******
--- /dev/null
+options:
+ --src .
+ --doc ./DocNot
+ --multidoc
+ --html
+ignore files:
+ CVS
+accept files:
+ *.pl
+headertypes:
+ u Unittests robo_unittest
+ x "System tests" robo_sys_tests
+ e "Makefile entries" robo_mk_entries
--- /dev/null
+#****h* ROBODoc/Makefile.plain
+# NAME
+# Makefile.plain -- Plain makefile that does not need autoconf
+# AUTHOR
+# Mäkelä
+#*****
--- /dev/null
+#****f* Test/test
+# NAME
+# Test
+#
+#
+#
+#
+#
+
+
+
+
+#****f* Test/test2
+
+
+
+
+
+
+# there is no end marker
--- /dev/null
+#****f* Test/test
+# NAME
+# Test
+#
+#
+#
+#
+#
+
+
+
+
+# there is no end marker
+# last line of the file...
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: filter_test_1.pl,v 1.2 2004/08/20 21:29:00 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("File Filter Test 1");
+ROBOTest::assertFile("Doc1/robo_functions.html");
+ROBOTest::assertFile("Doc1/test2_c.html");
+ROBOTest::assertDir("Doc1/ToBeAccepted");
+ROBOTest::assertNotDir("Doc1/ToBeIgnored");
+ROBOTest::assertNotFile("Doc1/test1_pas.html");
+ROBOTest::assertNotFile("Doc1/README.html");
+ROBOTest::finish;
+
--- /dev/null
+ignore files:
+ ??ADME
+ *.pas
+ ToBeIgnored
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: filter_test_2.pl,v 1.1 2004/08/20 21:52:47 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("File Filter Test 2");
+ROBOTest::assertFile("Doc3/robo_functions.html");
+ROBOTest::assertFile("Doc3/accept_c.html");
+ROBOTest::assertFile("Doc3/accept_pl.html");
+ROBOTest::assertDir("Doc3/AcceptDir");
+ROBOTest::assertFile("Doc3/AcceptDir/accept_h.html");
+ROBOTest::assertNotDir("Doc3/ToBeIgnored");
+ROBOTest::assertNotFile("Doc3/ignore_ign.html");
+ROBOTest::assertNotFile("Doc3/ignore_xxx.html");
+ROBOTest::finish;
+
--- /dev/null
+ignore files:
+ ToBeIgnored
+accept files:
+ *.c
+ *.pl
+ *.h
--- /dev/null
+/****** Test/Test
+ * NAME
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_1
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_2
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_3
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_4
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_5
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_6
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_7
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_8
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_9
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_10
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_11
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_12
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_13
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_14
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_15
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_16
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_17
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_18
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_19
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_20
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_21
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_22
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_23
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_24
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_25
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_26
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_27
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_28
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_29
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_30
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_31
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_32
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_33
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_34
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_35
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_36
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_37
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_38
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_39
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_40
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_41
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_42
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_43
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_44
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_45
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_46
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_47
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_48
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_49
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_50
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_51
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_52
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_53
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_54
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_55
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_56
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_57
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_58
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_59
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_60
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_61
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_62
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_63
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_64
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_65
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_66
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_67
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_68
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_69
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_70
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_71
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_72
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_73
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_74
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_75
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_76
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_77
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_78
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_79
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_80
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_81
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_82
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_83
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_84
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_85
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_86
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_87
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_88
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_89
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_90
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_91
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_92
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_93
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_94
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_95
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_96
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_97
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_98
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_99
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_100
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_101
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_102
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_103
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_104
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_105
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_106
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_107
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_108
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_109
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_110
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_111
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_112
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_113
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_114
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_115
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_116
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_117
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_118
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_119
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_120
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_121
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_122
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_123
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_124
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_125
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_126
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_127
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_128
+ * xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_129
+ ****/
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/header_size_test_pl
+# FUNCTION
+# Test whether robodoc can handle large headers. Robodoc uses a
+# dynamically expanding buffer for storing headers. This dynamic
+# expansion might go wrong. The input file for this test is
+# generated by the makefile entry header_size_test
+# SOURCE
+#
+
+ROBOTest::start("Header Size Test");
+
+# They are 129 lines of the form
+# xxxxxxxxx_<number>
+# in the header.
+# Lets test if they all turn up in the documentation.
+#
+my $checksum = 0;
+foreach my $n (1 .. 129) {
+ $checksum += $n;
+}
+my $sum = 0;
+while (my $line = <>) {
+ if ($line =~ m/xxxx_(\d+)/) {
+ $sum += $1;
+ }
+}
+
+ROBOTest::assert( $sum == $checksum );
+ROBOTest::finish;
+
+0;
+
+#*****
+
--- /dev/null
+/****m* Muppets/Header_1
+ *
+ * NAME
+ * testmark 1 testmark
+ * NAME
+CTest testmark 1000 testmark This should not appear
+ * test
+ * PURPOSE
+ * test
+ * SEE ALSO
+ * Puppet::walk
+ ******
+ */
+
+
+/****c* Muppets/Header_2 [1.0]
+ *
+ *
+ * NAME
+ * testmark 2 testmark
+ * NAME
+;*Test testmark 1001 testmark This should not appear
+ * SEE ALSO
+ * Puppet::talk
+ *****
+ */
+
+
+#****f* T_1_/Header_3
+# NAME
+# testmark 3 testmark
+# SOURCE
+
+tcl ()
+
+#***
+
+
+
+|****v* T_2_/Header_4
+| NAME
+| testmark 4 testmark
+| SOURCE
+
+assembler ()
+
+|****
+
+
+//****d* T_3_/Header_5
+// NAME
+// testmark 5 testmark
+// SOURCE
+
+c++ ()
+
+//****
+
+
+--****h* T_4_/Header_6
+-- NAME
+-- testmark 6 testmark
+-- SOURCE
+
+occam ()
+
+--***
+
+
+<!--****f* T_5_/Header_7
+* NAME
+* testmark 7 testmark
+*
+* SOURCE
+
+html ()
+
+<!--****-->
+
+
+
+REM ****f* T_6_/Header_8
+REM NAME
+REM testmark 8 testmark
+REM ***
+
+
+
+Rem ****f* T_7_/Header_9
+Rem NAME
+Rem testmark 9 testmark
+Rem ***
+
+
+#****f* T_7_/Header_10
+# NAME
+# testmark 10 testmark
+#***
+
+
+%****f* T_11_/Header_11
+% NAME
+% testmark 11 testmark
+%***
+
+
+(****f* T_12_/Header_12
+* NAME
+* testmark 12 testmark
+****)
+
+'****f* T_13_/Header_13
+'* NAME
+'* testmark 13 testmark
+'*****
+
+!!****f* T_14_/Header_14
+!! NAME
+!! MYSUB
+!! NOTES
+!! testmark 14 testmark
+!! SOURCE
+!!
+
+USE MYMODULE
+IMPLICIT NONE
+END IF
+
+!!******
+
+.****f* T_15_/Header_15
+.* NAME
+.* testmark 15 testmark
+.* NOTES
+.* no notes
+.****
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/header_test_pl
+# FUNCTION
+# Test if robodoc recognizes all the different headers in the
+# various languages that robodoc supports. The input file for this
+# test is generated with the makefile entry header_test
+# SOURCE
+#
+
+ROBOTest::start("Header Test");
+
+# We scan the output file for the headers.
+# Each header has a name item with the text
+# testmark <number> testmark
+# the name of each header has the form
+# Header_<number>
+# We compute the sum of all the numbers.
+
+# There are 15 headers numbered 1 .. 15
+my $checksum = 0;
+foreach my $n (1 .. 15) {
+ $checksum += $n;
+}
+
+my $sum = 0;
+while (my $line = <>) {
+ # The numbers between the marks.
+ if ($line =~ m/<PRE\S+\s+testmark\s+(\d+)\s/i) {
+ $sum += $1;
+ }
+ # These numbers in the names, listed in the table
+ # of content.
+ if ($line =~ m/<A HREF[^\/]+\/Header_(\d+)<\/A>/i) {
+ $sum += $1;
+ }
+}
+
+# Does each header number occur twice?
+ROBOTest::assert( $sum == (2 * $checksum) );
+ROBOTest::finish;
+
+0;
+
+#******
--- /dev/null
+/* (almost) Identical to header_test.dat except headers indented */
+ /****m* Muppets/Header_1
+ * NAME
+ * testmark 1 testmark
+ * PURPOSE
+ * test
+ * blah-blah-blah
+ * moi moi moi
+ * SEE ALSO
+ * Puppet::walk
+ ******
+ */
+
+
+ /****c* Muppets/Header_2 [1.0]
+ * NAME
+ * testmark 2 testmark
+ * SEE ALSO
+ * Puppet::talk
+ *****
+ */
+
+
+
+ #****f* T_1_/Header_3
+ # NAME
+ # testmark 3 testmark
+ # SOURCE
+
+ tcl ()
+
+ #***
+
+
+
+ |****f* T_2_/Header_4
+ | NAME
+ | testmark 4 testmark
+ | SOURCE
+
+ assembler ()
+
+ |****
+
+
+ //****f* T_3_/Header_5
+ // NAME
+ // testmark 5 testmark
+ // SOURCE
+
+ c++ ()
+
+ //****
+
+
+--****f* T_4_/Header_6
+-- NAME
+-- testmark 6 testmark
+-- SOURCE
+
+occam ()
+
+--***
+
+
+<!--****f* T_5_/Header_7
+* NAME
+* testmark 7 testmark
+*
+*SOURCE
+
+html ()
+
+<!--****-->
+
+
+
+ REM ****f* T_6_/Header_8
+ REM NAME
+ REM testmark 8 testmark
+ REM ***
+
+
+
+ Rem ****f* T_7_/Header_9
+ Rem NAME
+ Rem testmark 9 testmark
+ Rem ***
+
+
+ #****f* T_7_/Header_10
+ # NAME
+ # testmark 10 testmark
+ #***
+
+
+ %****f* T_11_/Header_11
+ % NAME
+ % testmark 11 testmark
+ %***
+
+
+ (****f* T_12_/Header_12
+ * NAME
+ * testmark 12 testmark
+ ****)
+
+
+
+#****f* Crash/crash
+# NAME
+# Used to crash on this
+# SOURCE
+foo
+#****
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use ROBOTest;
+ROBOTest::start("Indented Header Test");
+
+# There are 12 headers numbered 1 .. 12
+my $checksum = 0;
+foreach my $n (1 .. 12) {
+ $checksum += $n;
+}
+
+# Now we scan the output file for the headers
+# The should occur after a and compute the sum
+# of the header numbers
+my $sum = 0;
+# count the number of testmarkers and the number of
+# Headers in <A HREF> blocks (table of content)
+# and between the word "testmark" in the NAME item
+# We extract the header number and compute the sum.
+while (my $line = <>) {
+ if ($line =~ m/<PRE\S+\s+testmark\s+(\d+)\s/i) {
+ $sum += $1;
+ }
+ # These are in the table of content
+ if ($line =~ m/<A HREF[^\/]+\/Header_(\d+)<\/A>/i) {
+ $sum += $1;
+ }
+}
+# Dus each header number occur twice?
+ROBOTest::assert($sum == 2 * $checksum);
+ROBOTest::finish;
+
--- /dev/null
+#****f* Test/T1
+# NAME
+# __1__
+# FUNCTION
+# __2__
+#
+# SEE ALSO
+#
+# __3__
+#
+#******
+
+
+
+#****f* Test/T2
+# NAME
+# __4__
+# __5__
+# FUNCTION
+# __6__
+# __7__
+#
+# SEE ALSO
+#
+# __8__
+# __9__
+#
+#******
+
+#****f* Test/T1b
+# NAME
+#
+# __12__
+#
+# SEE ALSO
+# __11__
+#*****
+
+#****f* Test/T3
+# NAME
+#
+#
+# __13__
+# __14__
+# __15__
+#
+#
+# SEE ALSO
+#
+#
+#
+#
+# __16__
+# __17__
+#
+# __18__
+# SEE ALSO
+#
+# __19__
+# __20__
+# __10__
+#
+#******
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("Header Item Body Test");
+
+# There are a number of headers. They contain items that
+# have lines numbered __1__ to __20__
+# They should all show up.
+#
+my $checksum = 0;
+foreach my $n (1 .. 20) {
+ $checksum += $n;
+}
+
+# Now we scan the output file for the headers
+# find the numbers and add them...
+my $sum = 0;
+while (my $line = <>) {
+ if ($line =~ m/__(\d+)__/) {
+ $sum += $1;
+ }
+}
+
+# Does each number occur ?
+
+ROBOTest::assert( $sum == $checksum );
+ROBOTest::finish;
+
--- /dev/null
+
+#****f* A_Module_TEST/a_function_test
+# FUNCTION
+# A Test
+#*****
+
+#****f* A_Module_TEST/a function test
+# FUNCTION
+# A Test
+#*****
+
+#****f* A_Module_TEST/a function_test
+# FUNCTION
+# A Test ^^^^ evil extra spaces ^^^^
+#*****
+
+#****f* A Module TEST/a_function_test
+# FUNCTION
+# A Test
+#*****
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/header_test4_pl
+# FUNCTION
+# Test if robodoc can handle module and object names with
+# spaces in it. For this we process a file with four
+# headers looking something like
+# A_Module_TEST/a function test
+# Each header has the words A, Module, TEST, a, function, and
+# test. They are either separated with spaces or with a '_'.
+# ROBODoc should find them all.
+# SOURCE
+#
+
+ROBOTest::start("Names with Spaces Test");
+
+my $count = 0;
+while (my $line = <>) {
+ if ( $line =~ m/HEADER_MODULE\s+(\S.*?)$/ ) {
+ my $module = $1;
+ if ( ( $module =~ m/A/ ) and
+ ( $module =~ m/Module/ ) and
+ ( $module =~ m/TEST/ ) ) {
+ $count++;
+ }
+ }
+ if ( $line =~ m/HEADER_FUNCTION_NAME\s+(\S.*?)$/ ) {
+ my $object = $1;
+ if ( ( $object =~ m/a/ ) and
+ ( $object =~ m/function/ ) and
+ ( $object =~ m/test/ ) ) {
+ $count++;
+ }
+ ROBOTest::assert( $object !~ m/\s+$/, "no spaces at end of name" );
+ }
+}
+
+# There are 4 valid headers, so the count should be 8
+ROBOTest::assert( $count == 8, "All 4 headers found" );
+ROBOTest::finish;
+
+0;
+
+#******
--- /dev/null
+#****f* T5Test/test
+# FUNCTION
+# Test oo1
+# SOURCE
+testmark 1
+# testmark 2
+testmark 3
+#
+# NOTES
+# testmark 4
+# NAME
+# testmark 5
+#****
+
+#****f* T5Test/test2
+# FUNCTION
+# Test
+# SOURCE
+#
+testmark 1
+# testmark 2
+testmark 3
+# NOTES
+# testmark 4
+# NAME
+# testmark 5
+#****
+
+#****f* T5Test/test3
+# FUNCTION
+# Test
+ testmark 2100 should not be in the doc
+ testmark 2200 should not be in the doc
+# SYNOPSIS
+# testmark 1
+# testmark 2
+testmark 3
+# NOTES
+# testmark 4
+# NAME
+# testmark 5
+# SOURCE
+#
+ aaaa
+ bbbb
+ cccc
+ dddd
+ eeee
+#****
+
+testmark 33000 should not be in the doc
+
+
+
+/****f* T5Test/test4
+ * FUNCTION
+ * Test
+ testmark 2100 should not be in the doc
+ testmark 2200 should not be in the doc
+ * SYNOPSIS
+ */
+/* testmark 1 */
+/* testmark 2 */
+
+testmark 3
+
+/*
+ * NOTES
+ * testmark 4
+ * NAME
+ * testmark 5
+ * SOURCE
+ */
+ aaaa
+ bbbb
+ cccc
+ dddd
+ eeee
+/*****/
+
+testmark 33001 should not be in the doc
+
+
+/*****/
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/header_test5_pl
+# FUNCTION
+# ROBODoc allows a user to define that some items should
+# work similar to the source item. These tests check wether
+# this works.
+# SOURCE
+#
+
+ROBOTest::start("Source Item Test");
+
+# We scan the output file for the headers.
+# In each header there are items that contains
+# testmark 1 testmark
+# testmark 2 testmark
+# testmark 3 testmark
+# testmark 4 testmark
+# testmark 5 testmark
+# in such locations that they end-up in the final
+# documentation. We scan the final documentation
+# and collect and the numbers between the test
+# marks.
+
+my $checksum = 1 + 2 + 3 + 4 + 5;
+
+my $sum = 0;
+my $sum_empty_remark = 0;
+
+while (my $line = <>) {
+ # The numbers between the marks.
+ if ( $line =~ m/testmark\s(\d+)/ ) {
+ $sum += $1;
+ }
+ # There should be no empty '/*' or '*/' in the documentation
+ # robodoc is supposed to remove these from the begin and
+ # end of a 'source' item.
+
+ # No /*
+ if ( $line =~ m/^\s*\/\*\s*$/ ) {
+ $sum_empty_remark++;
+ }
+ # No */
+ if ( $line =~ m/^\s\*\/\s*$/ ) {
+ $sum_empty_remark++;
+ }
+}
+
+# There are 4 headers so:
+ROBOTest::assert( $sum == (4 * $checksum), "All source is included" );
+ROBOTest::assert( $sum_empty_remark == 0, "No empty remark begin or ends" );
+ROBOTest::finish;
+#
+
+0;
+
+#******
--- /dev/null
+
+/****f* bad1&bad1/bad2>>bad2
+ * NAME
+ * bad2>>bad2
+ * SEE ALSO
+ * bad3<<bad3
+ ******
+ */
+
+
+/****f* bad1&bad1/bad3<<bad3
+ * NAME
+ * bad3<<bad3
+ * SEE ALSO
+ * bad2>>bad2
+ ******
+ */
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("HTML Special Char Test");
+
+# In the headers there are three bad names
+# bad1&bad1
+# bad2>>bad2
+# and
+# bad3<<bad3
+#
+# These should all have been escaped and no longer occur
+# in the output
+
+my $count = 0;
+while (my $line = <>) {
+ if ($line =~ m/bad\d([^b]+)bad/) {
+ my $string = $1;
+ if (($string eq ">>") or
+ ($string eq "<<") or
+ ($string eq "&")) {
+ ++$count;
+ }
+ }
+}
+
+ROBOTest::assert($count == 0);
+ROBOTest::finish;
--- /dev/null
+/****f* Test/Test
+ * NAME
+ * test appear
+ * FUNCTION
+ * should not be displayed
+ * should not be displayed
+ * should not be displayed
+ * should not be displayed
+ * SEE ALSO
+ * should not be displayed
+ * RETURN VALUE
+ * test appear
+ *****/
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Test whether robodoc can handle the --lock mode
+# In the lock mode it will lock on to one kind of header marker
+# per file and one kind of remark marker per header. All
+# other ones should be skipped.
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("ignore items: Test");
+my $checksum_not_appear = 0;
+my $checksum_appear = 0;
+while (my $line = <>) {
+ if ($line =~ m/not\sbe\sdisplayed/) {
+ ++$checksum_not_appear;
+ }
+ if ( $line =~ m/appear/ ) {
+ ++$checksum_appear;
+ }
+}
+ROBOTest::assert( $checksum_not_appear == 0 );
+ROBOTest::assert( $checksum_appear == 2 );
+ROBOTest::finish;
+
--- /dev/null
+ignore items:
+ FUNCTION
+ SEE ALSO
--- /dev/null
+/* Classes */
+
+/****ic* InternalHeaderTest/InternalClass_it_1
+ * NAME
+ * InternalClass_ --
+ *****/
+
+/****ic* InternalHeaderTest/InternalClass_2_it_2
+ * NAME
+ * InternalClass_2 --
+ *****/
+
+/****c* InternalHeaderTest/Class
+ * NAME
+ * Class --
+ *****/
+
+/****c* InternalHeaderTest/Class_2
+ * NAME
+ * Class_2 --
+ *****/
+
+/* Headers */
+
+/****h* InternalHeaderTest/Header
+ * NAME
+ * Header --
+ *****/
+
+/****ih* InternalHeaderTest/InternalHeader_it_3
+ * NAME
+ * InternalHeader --
+ *****/
+
+/* Methods */
+
+/****m* InternalHeaderTest/Method
+ * NAME
+ * Method --
+ *****/
+
+/****im* InternalHeaderTest/InternalMethod_it_4
+ * NAME
+ * InternalMethod --
+ *****/
+
+/* Functions */
+
+/****f* InternalHeaderTest/Function
+ * NAME
+ * Function --
+ *****/
+
+/****if* InternalHeaderTest/InternalFunction_it_5
+ * NAME
+ * InternalFunction --
+ *****/
+
+/* Constants */
+
+/****d* InternalHeaderTest/Constant
+ * NAME
+ * Constant --
+ *****/
+
+/****id* InternalHeaderTest/InternalConstant_it_6
+ * NAME
+ * InternalConstant --
+ *****/
+
+/* Variable */
+
+/****v* InternalHeaderTest/Variable
+ * NAME
+ * Variable --
+ *****/
+
+/****iv* InternalHeaderTest/InternalVariable_it_7
+ * NAME
+ * InternalVariable --
+ *****/
+
+/* Structures */
+
+/****s* InternalHeaderTest/Structure
+ * NAME
+ * Structure --
+ *****/
+
+/****is* InternalHeaderTest/InternalStructure_it_8
+ * NAME
+ * InternalStructure --
+ *****/
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#****x* SystemTest/internalheader_test
+# FUNCTION
+# Test to see if robodoc handles all the internal
+# headers and the options to go with it correctly.
+# For this we created a testfile with internal and
+# normal headers. We generate the documentation for
+# it and check if the internal headers show up.
+# SOURCE
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("Internalheader Test");
+
+# There are 8 internal headers numbered 1 .. 8 in the .dat file
+my $checksum = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
+
+# We now extract them and compute the sum of all the numbers
+my %seen = ();
+
+while (my $line = <>) {
+# the internal header names end with it_1, it_2, etc, so
+# we extract these numbers.
+ if ($line =~ m/it_(\d+)</) {
+ $seen{$1}++;
+ }
+}
+my $sum = 0;
+foreach my $key (keys %seen) {
+ $sum += $key;
+}
+
+ROBOTest::assert($sum == $checksum);
+ROBOTest::finish;
+
+#*******
+
--- /dev/null
+internalheader_test.xref
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Test to see if robodoc handles all the internal
+# headers and the options to go with it correctly.
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("Internalheader Test 2");
+
+# There are 8 internal headers numbered 1 .. 8 in the .dat file
+# None of them should occur in the output.
+my $checksum = 0;
+
+# We now extract them and compute the sum of all the numbers
+my %seen = ();
+
+while (my $line = <>) {
+ if ($line =~ m/it_(\d+)</) {
+ $seen{$1}++;
+ }
+}
+
+my $sum = 0;
+foreach my $key (keys %seen) {
+ $sum += $key;
+}
+ROBOTest::assert( $sum == $checksum);
+ROBOTest::finish;
--- /dev/null
+/****** ROBODoc/ItemsExamples
+ * NAME
+ * testmark 1 testmark
+ * COPYRIGHT
+ * testmark 2 testmark
+ * SYNOPSIS
+ * testmark 3 testmark
+ * USAGE
+ * testmark 4 testmark
+ * FUNCTION
+ * testmark 5 testmark
+ * DESCRIPTION
+ * testmark 6 testmark
+ * PURPOSE
+ * testmark 7 testmark
+ * AUTHOR
+ * testmark 8 testmark
+ * CREATION DATE
+ * testmark 9 testmark
+ * MODIFICATION HISTORY
+ * testmark 10 testmark
+ * HISTORY
+ * testmark 11 testmark
+ * INPUTS
+ * testmark 12 testmark
+ * ARGUMENTS
+ * testmark 13 testmark
+ * OPTIONS
+ * testmark 14 testmark
+ * PARAMETERS
+ * testmark 15 testmark
+ * SWITCHES
+ * testmark 16 testmark
+ * OUTPUT
+ * testmark 17 testmark
+ * SIDE EFFECTS
+ * testmark 18 testmark
+ * RESULT
+ * testmark 19 testmark
+ * RETURN VALUE
+ * testmark 20 testmark
+ * EXAMPLE
+ * testmark 21 testmark
+ * NOTES
+ * testmark 22 testmark
+ * DIAGNOSTICS
+ * testmark 23 testmark
+ * WARNINGS
+ * testmark 24 testmark
+ * ERRORS
+ * testmark 25 testmark
+ * BUGS
+ * testmark 26 testmark
+ * TODO
+ * testmark 27 testmark
+ * IDEAS
+ * testmark 28 testmark
+ * PORTABILITY
+ * testmark 29 testmark
+ * SEE ALSO
+ * testmark 30 testmark
+ * METHODS
+ * testmark 31 testmark
+ * NEW METHODS
+ * testmark 32 testmark
+ * ATTRIBUTES
+ * testmark 33 testmark
+ * NEW ATTRIBUTES
+ * testmark 34 testmark
+ * TAGS
+ * testmark 35 testmark
+ * COMMANDS
+ * testmark 36 testmark
+ * DERIVED FROM
+ * testmark 37 testmark
+ * DERIVED BY
+ * testmark 38 testmark
+ * USES
+ * testmark 39 testmark
+ * CHILDREN
+ * testmark 40 testmark
+ * USED BY
+ * testmark 41 testmark
+ * PARENTS
+ * testmark 42 testmark
+ * SOURCE
+ */
+
+ testmark 43 testmark
+
+/******/
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use ROBOTest;
+
+ROBOTest::start("Item Test");
+
+#
+# In the .dat file there are 43 items numbered from 1 .. 43
+# The number is located between the words testmark in the item text
+#
+my $checksum = 0;
+foreach my $n (1 .. 43) {
+ $checksum += $n;
+}
+
+# We now scan the .res file find the item number, compute the
+# sum, and see if this is equal to the checksum...
+my $sum = 0;
+while (my $line = <>) {
+ if ($line =~ m/<PRE\S+\s+testmark\s+(\d+)\s/) {
+ $sum += $1;
+ }
+}
+
+# there should be 43 different items
+#
+ROBOTest::assert($sum = $checksum, "Are all items there?");
+ROBOTest::finish;
--- /dev/null
+
+/****f* bad1$bad1/bad2$bad2
+ * NAME
+ * bad2[bad2
+ * SEE ALSO
+ * bad3%bad3
+ ******
+ */
+
+
+/****f* bad1$bad1/bad3%bad3
+ * NAME
+ * bad3%bad3
+ * SEE ALSO
+ * bad2[bad2
+ ******
+ */
+
+/****f* bad1$bad1/bad4$bad4
+ * NAME
+ * bad4]bad4
+ * SEE ALSO
+ * bad2[bad2
+ *
+ ******
+ */
+
+/****f* bad1$bad1/bad5_bad5
+ * NAME
+ * bad5_bad5
+ * SEE ALSO
+ * bad2$bad2
+ * bad3%bad3
+ * bad4$bad4
+ ******
+ */
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("LaTeX Special Char Test");
+
+# In the headers there are three bad names
+# bad1$bad1
+# bad2$bad2
+# bad3%bad3
+# bad4$bad4
+# bad5_bad5
+#
+# These should all have been escaped and no longer occur
+# in the output
+
+my $count = 0;
+while (my $line = <>) {
+ if (($line =~ m/begin{verbatim/) ..
+ ($line =~ m/end{verbatim/)) {
+
+ } else {
+ if ($line =~ m/bad\d([^b]+)bad/) {
+ my $string = $1;
+ if (($string eq "\$") or
+ ($string eq "_") or
+ ($string eq "%")) {
+ ++$count;
+ }
+ }
+ }
+}
+
+ROBOTest::assert($count == 0);
+ROBOTest::finish;
--- /dev/null
+/****f* LinkTest/link_1_
+ * NAME
+ * link_1_
+ ******/
+
+
+/****f* _LinkTest_/_link_2_
+ * NAME
+ * _link_2_
+ ******/
+
+
+/****f* LinkTest/link3
+ * NAME
+ * link3
+ ******/
+
+
+/****f* LinkTest/-link4
+ * NAME
+ * -link4
+ ******/
+
+/****f* LinkTest/Llink5
+ * NAME
+ * Llink5
+ ******/
+
+
+/****f* LinkTest/thelinks1
+ * NAME
+ * thelink
+ * SEE ALSO
+ * A link at the end of the line link_1_.
+ * A link as function link_1_(test).
+ * link_1_ as the begin of a sentence.
+ * A comma sentence link_1_, this is.
+ *****/
+
+
+/****f* LinkTest/thelinks2
+ * NAME
+ * thelink
+ * SEE ALSO
+ * A link at the end of the line _link_2_.
+ * A link as function _link_2_(test).
+ * _link_2_ as the begin of a sentence.
+ * A comma sentence _link_2_, this is.
+ *****/
+
+
+/****f* LinkTest/thelinks3
+ * NAME
+ * thelink
+ * SEE ALSO
+ * A link at the end of the line link3.
+ * A link as function link3(test).
+ * link3 as the begin of a sentence.
+ * A comma sentence link3, this is.
+ *****/
+
+
+/****f* LinkTest/thelinks4
+ * NAME
+ * thelink
+ * SEE ALSO
+ * A link at the end of the line -link4.
+ * A link as function -link4(test).
+ * -link4 as the begin of a sentence.
+ * A comma sentence -link4, this is.
+ *****/
+
+/****f* LinkTest/thelinks5
+ * NAME
+ * thelink
+ * SEE ALSO
+ * A link at the end of the line Llink5.
+ * A link as function Llink5(test).
+ * Llink5 as the begin of a sentence.
+ * A comma sentence Llink5, this is.
+ *****/
+
+
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+use ROBOTest;
+
+#****x* SystemTest/link_test_pl
+# FUNCTION
+# Test if ROBODoc correctly creates links between words
+# and headers.
+#
+# There are four links, link_1_, _link_2_, link3, and -link4
+# they should each occur four times in a <A HREF></A>
+# SOURCE
+#
+
+
+ROBOTest::start("Link Test");
+
+my %count = ();
+$count{"link_1_"} = 0;
+$count{"_link_2_"} = 0;
+$count{"link3"} = 0;
+$count{"-link4"} = 0;
+$count{"Llink5"} = 0;
+
+while (my $line = <>) {
+ if ($line =~ m#<A\sHREF=[^>]+>([^<]+)</A>#i) {
+ my $link_name = $1;
+ ++$count{$link_name};
+ }
+}
+
+ROBOTest::assert(
+ ($count{"link_1_"} == 4) and
+ ($count{"_link_2_"} == 4) and
+ ($count{"link3"} == 4) and
+ ($count{"-link4"} == 4) and
+ ($count{"Llink5"} == 4),
+ "linking");
+
+#*****
--- /dev/null
+Program Test
+!!****f* main/test
+!! NAME
+!! Testing 1 2 3
+!! Program Test
+ Test1 = ABC &
+ // det ! This should not be displayed
+ Test2 = ABC &
+ * detail ! This should not be displayed
+ Test3 = ABC + &
+ C * D ! This should not be displayed
+ TEst4 = ABC * &
+ REM * B ! This should not be displayed
+ Test5 = &
+ 'Test' ! This should not be displayed
+ End Program Test
+!!***
+
+
+/****f* main/test2
+ * NAME
+ * This should not be displayed
+ * FUNCTION
+ * This should not be displayed
+ * SOURCE
+ */
+
+This should not be displayed
+This should not be displayed
+
+/******/
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Test whether robodoc can handle the --lock mode
+# In the lock mode it will lock on to one kind of header marker
+# per file and one kind of remark marker per header. All
+# other ones should be skipped.
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("--lock Test");
+my $checksum = 0;
+
+while (my $line = <>) {
+ if ($line =~ m/not\sbe\sdisplayed/) {
+ ++$checksum;
+ }
+}
+ROBOTest::assert( $checksum == 0 );
+ROBOTest::finish;
+
--- /dev/null
+#****h* ROBODoc/SystemTest
+# DESCRIPTION
+# The systemtest is a series of tests that test the
+# behaviour of robodoc and robohdrs.
+#
+# Most tests consists of:
+# * One or more data files (.dat)
+# * One or more result files (.html etc). The result files are generated
+# by running robodoc on the .dat files. Or sometimes on whole
+# directories with source files.
+# * A perl script that checks the results and prints either OK or FAIL
+# For instance there is a test that checks if robodoc correctly
+# recognizes all Item names. The data files for this is
+# item_test.dat. It contains all item names with in between
+# testmark dd
+# where dd is a number from 1 to 43
+#
+# Robodoc is run on this data file and a file
+# called item_test.html is generated. A script called item_test.pl
+# scans this .html file finds all the "testmark dd"s and check if
+# all number from 1 to 43 occur. If so it prints OK if not it
+# print FAIL.
+#
+# To run the test do a
+# make
+#
+# To cleanup all the generated files do a
+# make clean
+#
+#*****
+# $Id: makefile,v 1.54 2007/01/29 22:23:53 gumpu Exp $
+#
+
+
+# The programs to be tested.
+#
+ROBODOC = ../robodoc
+ROBOHDRS = ../robohdrs
+
+# No unnecessary output.
+.SILENT :
+
+all: permissions clean test
+
+# TODO header_test4
+# TODO header_test5
+
+test : item_test \
+ header_test \
+ header_test2 \
+ header_test3 \
+ url_test \
+ header_size_test \
+ cmode_test \
+ link_test \
+ html_specialchar_test \
+ internalheader_test \
+ internalheader_test2 \
+ latex_specialchar_test \
+ remarkmarker_test \
+ multidoc_test1 \
+ encoding_test \
+ tabtest \
+ pipe_test \
+ lock_test \
+ user_specified_css_file_test \
+ master_index_test \
+ robohdrs_test \
+ filter_test_1 \
+ filter_test_2 \
+ sort_test \
+ nosource_test \
+ ignore_test \
+ nosort_test \
+ forth_test \
+ doc_dir_filter_test
+
+# temporarily excluded: parameter_test
+
+#
+# TODO
+# outputformat_test
+# These test error handling, but they are not
+# finished yet.
+# error_missing_header_end_test
+# error_missing_header_end_test_2
+
+
+permissions:
+ chmod 755 *.pl
+
+clean: doc1_clean doc2_clean
+ -rm -f *.res *.html *.tex *.rtf *.xml *.toc *.log *.idx *.dvi *.aux *.pdf
+ -rm -f *.tst
+ -rm -rf outputformat_test.txt
+ -rm -rf *.stderr
+ -rm -rf RoboHeaders/*.c
+ -rm -rf robohdrs_test.hdrs
+ -rm -rf cmode_test1.css
+ -rm -rf header_size_test.css
+ -rm -rf header_test2.css
+ -rm -rf header_test3.css
+ -rm -rf header_test4.css
+ -rm -rf header_test.css
+ -rm -rf html_specialchar_test2.css
+ -rm -rf html_specialchar_test.css
+ -rm -rf internalheader_test2.css
+ -rm -rf internalheader_test.css
+ -rm -rf item_test.css
+ -rm -rf link_test.css
+ -rm -rf lock_test.css
+ -rm -rf outputformat_test.css
+ -rm -rf parameter_test.css
+ -rm -rf pipe_test.css
+ -rm -rf remarkmarker_test.css
+ -rm -rf tabtest.css
+ -rm -rf url_test.css
+ -rm -rf forth_test.css
+ -rm -rf error_test.css
+ -rm -rf ignore_test.css
+ -rm -rf nosource_test.css
+
+#
+#
+# Test the handling of HTML reserver characters.
+#
+html_specialchar_test : html_specialchar_test.html html_specialchar_test2.html
+ ./$(@F).pl < html_specialchar_test.html
+ ./$(@F).pl < html_specialchar_test2.html
+
+html_specialchar_test.html : html_specialchar_test.dat
+ $(ROBODOC) --src html_specialchar_test.dat --doc html_specialchar_test --html --singlefile
+
+html_specialchar_test2.html : html_specialchar_test.dat
+ $(ROBODOC) --src html_specialchar_test.dat --doc html_specialchar_test2 --html --singlefile --sections --toc
+
+#****e* SystemTest/latex_specialchar_test
+# FUNCTION
+# Create input for testing the handling of latex reserved characters
+# SOURCE
+#
+latex_specialchar_test : latex_specialchar_test.tex latex_specialchar_test2.tex
+ ./$(@F).pl < latex_specialchar_test.tex
+ ./$(@F).pl < latex_specialchar_test2.tex
+
+latex_specialchar_test.tex : latex_specialchar_test.dat
+ $(ROBODOC) --src latex_specialchar_test.dat --doc latex_specialchar_test --latex --singlefile
+
+latex_specialchar_test2.tex : latex_specialchar_test.dat
+ $(ROBODOC) --src latex_specialchar_test.dat --doc latex_specialchar_test2 --latex --singlefile --sections --toc
+
+#******
+
+
+#****e* SystemTest/header_size_test
+# FUNCTION
+# Create the input file for header_size_test_pl.
+# SOURCE
+#
+header_size_test : header_size_test.html
+ ./$(@F).pl < header_size_test.html
+
+header_size_test.html : header_size_test.dat
+ $(ROBODOC) --src $< --doc header_size_test --html --singlefile
+
+#*****
+
+
+#****e* SystemTest/internalheader_test, internalheader_test2
+# FUNCTION
+# Create the input for the internalheader_test.
+# SOURCE
+#
+internalheader_test : internalheader_test.html
+ ./$(@F).pl < internalheader_test.html
+
+internalheader_test.html : internalheader_test.dat
+ $(ROBODOC) --src internalheader_test.dat --doc internalheader_test --html --singlefile --index --toc --internal
+
+#
+internalheader_test2 : internalheader_test2.html
+ ./$(@F).pl < internalheader_test2.html
+
+internalheader_test2.html : internalheader_test.dat
+ $(ROBODOC) --src internalheader_test.dat --doc internalheader_test2 --html --singlefile --index --toc
+
+#**********
+
+
+#
+#
+outputformat_test : outputformat_test.html \
+ outputformat_test.txt \
+ outputformat_test.rtf
+# outputformat_test_dbsgml.res \
+# outputformat_test_troff.res
+ ./$(@F).pl
+
+
+outputformat_test.html : outputformat_test.dat
+ $(ROBODOC) --src $< --doc outputformat_test --singlefile --html
+
+outputformat_test.txt : outputformat_test.dat
+ $(ROBODOC) --src $< --doc outputformat_test --singlefile --ascii
+
+outputformat_test.rtf : outputformat_test.dat
+ $(ROBODOC) --src $< --doc outputformat_test --singlefile --rtf
+
+# outputformat_test.dbsgml : outputformat_test.dat
+# $(ROBODOC) --src $< --doc $@ DBSGML
+#
+# outputformat_test_troff.res : outputformat_test.dat
+# $(ROBODOC) --src $< --doc $@ TROFF
+
+
+#
+# Test if we recognize all predefined items.
+#
+item_test : item_test.html
+ ./$(@F).pl < item_test.html
+
+item_test.html : item_test.dat
+ $(ROBODOC) --src item_test.dat --doc item_test --html --singlefile
+
+#
+# Test the linking of keywords to headers.
+#
+link_test : link_test.html
+ ./$(@F).pl < link_test.html
+
+link_test.html : link_test.dat
+ $(ROBODOC) --src link_test.dat --doc link_test --html --singlefile
+
+#****e* SystemTest/header_test, header_test2, header_test3, header_test4, header_test5
+# FUNCTION
+# Create the input file for header_test_pl.
+# SOURCE
+#
+header_test : header_test.html
+ ./$(@F).pl < header_test.html
+
+header_test.html : header_test.dat
+ $(ROBODOC) --src header_test.dat --doc header_test --html --toc --singlefile
+
+
+#
+# indented header test case
+#
+header_test2 : header_test2.html
+ ./header_test2.pl < header_test2.html
+
+header_test2.html : header_test2.dat
+ $(ROBODOC) --src header_test2.dat --doc header_test2 --html --toc --singlefile
+
+#
+# indented header test case
+#
+header_test3 : header_test3.html
+ ./header_test3.pl < header_test3.html
+
+header_test3.html : header_test3.dat
+ $(ROBODOC) --src header_test3.dat --doc header_test3 --html --toc --singlefile
+
+header_test4 : header_test4.tst
+ ./$(@F).pl < header_test4.tst
+
+header_test4.tst : header_test4.dat
+ $(ROBODOC) --src header_test4.dat --doc header_test4 --test --toc --singlefile
+
+header_test5 : header_test5.tst
+ ./$(@F).pl < header_test5.tst
+
+header_test5.tst : header_test5.dat
+ $(ROBODOC) --rc header_test5.rc --src header_test5.dat --doc header_test5 --test --toc --singlefile
+
+#*************
+
+#
+# Test if we can recognize all the different remark markers for
+# all the languages we defined.
+#
+remarkmarker_test : remarkmarker_test.html
+ ./$(@F).pl < remarkmarker_test.html
+
+remarkmarker_test.html : remarkmarker_test.dat
+ $(ROBODOC) --src remarkmarker_test.dat --doc remarkmarker_test --html --singlefile
+
+#
+# Test the automatic recognition of urls
+#
+url_test : url_test.html
+ ./$(@F).pl < url_test.html
+
+url_test.html : url_test.dat
+ $(ROBODOC) --src url_test.dat --doc url_test --html --singlefile
+
+#
+# Test the syntax colouring of C code.
+#
+cmode_test : cmode_test1.html
+ ./cmode_test1.pl < cmode_test1.html
+
+cmode_test1.html : cmode_test.dat
+ $(ROBODOC) --src cmode_test.dat --doc cmode_test1 --html --cmode --singlefile
+
+#
+# Test the --multidoc mode
+#
+#
+multidoc_test1 : multidoc_test1.html
+ ./multidoc_test1.pl
+
+multidoc_test1.html :
+ $(ROBODOC) --src ./Scr1 --doc ./Doc1 --html --multidoc --index --sections --toc
+ touch multidoc_test1.html
+
+
+#
+# This tests if specifying the tabsize with --tabsize works.
+#
+tabtest : tabtest.html
+ ./$(@F).pl < tabtest.html
+
+tabtest.html : tabtest.dat
+ $(ROBODOC) --src tabtest.dat --doc tabtest --html --singlefile --tabsize 4
+
+#
+# This test if we can specify a full path to the source file
+# when we use --singlefile. (This borrows the code from tabtest)
+#
+parameter_test : parameter_test.html
+ ./$(@F).pl
+
+parameter_test.html : parameter_test.dat
+ $(ROBODOC) --src /home/robodoc/robo/Source/Test/parameter_test.dat --doc /home/robodoc/robo/Source/Test/parameter_test --html --singlefile
+
+
+# The xml file uses UTF-8 encoding. Although xmlto seems not to _write_
+# UTF-8, it does _read_ that correctly in input and complains if that is
+# not proper UTF-8 format.
+
+encoding_test: encoding_test.html
+ @ if [ -x "`which xmlto`" ] ; then \
+ xmlto html-nochunks encoding_test.xml ; \
+ else \
+ echo "xmlto missing, can not perform test"; \
+ fi ;
+#rm -f encoding_test.html encoding_test.xml encoding_test.pdf
+
+# Source file uses Latin-1 i.e. iso-8859-1 encoding
+encoding_test.html: encoding_test.dat
+ $(ROBODOC) --src encoding_test.dat --doc encoding_test --singlefile --dbxml
+
+
+#
+# Test if we can specify an alternative robodoc.rc file using
+# the --rc option.
+#
+
+option_rc_test: option_rc_test.html
+ ./$(@F).pl < option_rc_test.html
+
+option_rc_test.html: alt_rc_file.rc
+ $(ROBODOC) --rc alt_rc_file.rc --html
+
+
+#
+# Test the error generated for a header end messing from a header
+# that is not the last header in a file.
+#
+
+error_missing_header_end_test: error_missing_header_end_test.stderr
+ echo "TODO"
+
+error_missing_header_end_test.stderr : error_missing_header_end_test.dat
+ -$(ROBODOC) --src error_missing_header_end_test.dat --doc error_test --singlefile --html 2> error_missing_header_end_test.stderr
+
+#
+# Test the error generated for a header end missing from a header
+# that _is_ the last header in a file.
+#
+
+error_missing_header_end_test_2: error_missing_header_end_test_2.stderr
+ echo "TODO"
+
+error_missing_header_end_test_2.stderr : error_missing_header_end_test_2.dat
+ -$(ROBODOC) --src error_missing_header_end_test_2.dat --doc error_test --singlefile --html 2> error_missing_header_end_test_2.stderr
+
+
+#
+# The if a user can specify its own css file.
+#
+
+user_specified_css_file_test : user_specified_css_file_test.html
+ ./$(@F).pl < ./Doc1/robodoc.css
+
+user_specified_css_file_test.html : user_css.css
+ $(ROBODOC) --src ./Scr1 --doc ./Doc1 --html --multidoc --index --sections --toc --css user_css.css
+# touch user_specified_css_file_test.html
+
+
+lock_test : lock_test.html
+ ./$(@F).pl < lock_test.html
+
+lock_test.html: lock_test.dat
+ $(ROBODOC) --src lock_test.dat --doc lock_test --singlefile --lock --html
+
+#
+# Test the forth headers
+#
+
+forth_test: forth_test.html
+
+forth_test.html:
+ $(ROBODOC) --src ../../Headers/forth.sample --doc forth_test --singlefile --html --toc
+
+#
+#
+#
+
+pipe_test :
+ $(ROBODOC) --src pipe_test.dat --doc pipe_test --singlefile --html
+ ./pipe_test.pl 1 0 < pipe_test.html
+ ./pipe_test.pl 2 1 < pipe_test.html
+ ./pipe_test.pl 3 0 < pipe_test.html
+ $(ROBODOC) --src pipe_test.dat --doc pipe_test --singlefile --latex
+ @ if [ -x "`which latex`" ] ; then \
+ latex pipe_test.tex > /dev/null 2>&1 ; rm -f pipe_test/dvi ; \
+ else \
+ echo "latex missing, can not perform test"; \
+ fi ;
+ ./pipe_test.pl 2 0 < pipe_test.tex
+ ./pipe_test.pl 1 1 < pipe_test.tex
+
+#
+#
+#
+master_index_test : Doc2/masterindex.html
+ ./$(@F).pl
+
+Doc2/masterindex.html : Scr2/master_index_test.dat
+ $(ROBODOC) --src Scr2 --doc Doc2 --html --multidoc --index --internal
+
+
+#
+#
+#
+#
+RoboHeaders/rbh_test1.c : RoboHeaders/rbh_test1.dat
+ cp RoboHeaders/rbh_test1.dat RoboHeaders/rbh_test1.c
+
+robohdrs_test.hdrs : RoboHeaders/rbh_test1.c
+ cd RoboHeaders; ../$(ROBOHDRS) -p HDRS rbh_test1.c ; cd ..
+ touch robohdrs_test.hdrs
+
+
+#
+#
+#
+#
+robohdrs_test.doc : robohdrs_test.hdrs
+ -rm -rf Doc1/*.*
+ $(ROBODOC) --src RoboHeaders --doc Doc1 --html --multidoc --index
+
+robohdrs_test : robohdrs_test.doc
+ ./$(@F).pl
+
+
+doc_dir_filter_test.doc : doc_dir_filter_test.rc
+ $(ROBODOC) --rc doc_dir_filter_test.rc
+ $(ROBODOC) --rc doc_dir_filter_test.rc
+
+doc_dir_filter_test : doc_dir_filter_test.doc
+ ./$(@F).pl
+ -rm -rf DocNot
+
+
+
+#
+#
+#
+filter_test_2.doc :
+ -rm -rf Doc3/*
+ $(ROBODOC) --src FileFilterSrc_2 --doc Doc3 --html --multidoc --index --rc filter_test_2.rc
+
+filter_test_2 : filter_test_2.doc
+ ./$(@F).pl
+
+#
+#
+#
+filter_test_1.doc :
+ -rm -rf Doc1/*
+ $(ROBODOC) --src FileFilterSrc --doc Doc1 --html --multidoc --index --rc filter_test_1.rc
+
+filter_test_1 : filter_test_1.doc
+ ./$(@F).pl
+
+
+#
+#
+#
+sort_test.doc :
+ -rm -rf Doc1/*.*
+ $(ROBODOC) --src SortSource --doc Doc1 --html --multidoc --index --toc
+
+sort_test : sort_test.doc
+ ./$(@F).pl
+
+#
+#
+#
+nosort_test.doc :
+ -rm -rf Doc1/*.*
+ $(ROBODOC) --src SortSource --doc Doc1 --html --multidoc --index --toc --nosort
+
+nosort_test : nosort_test.doc
+ ./$(@F).pl
+
+
+nosource_test : nosource_test.html
+ ./$(@F).pl < nosource_test.html
+
+nosource_test.html : nosource_test.dat
+ $(ROBODOC) --src nosource_test.dat --doc nosource_test --singlefile --nosource --html
+
+ignore_test : ignore_test.html
+ ./$(@F).pl < ignore_test.html
+
+ignore_test.html : ignore_test.dat
+ $(ROBODOC) --src ignore_test.dat --doc ignore_test --singlefile --html --rc ignore_test.rc
+
+
+doc1_clean :
+ rm -rf Doc1/Subdir
+ rm -rf Doc1/ToBeAccepted
+ rm -rf Doc1/*.*
+
+doc2_clean :
+ rm -rf Doc2/*.*
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: master_index_test.pl,v 1.1 2003/12/14 17:45:10 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Masterindex Test");
+ROBOTest::assertFile("Doc2/robo_functions.html");
+ROBOTest::assertFile("Doc2/robo_variables.html");
+ROBOTest::finish;
+
--- /dev/null
+#!/usr/bin/perl -w
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Multidoc Test");
+
+use IO::File;
+
+
+
+sub Fail {
+ my $ok = shift;
+ if ( $ok ) {
+ print "OK\n";
+ } else {
+ print "FAIL\n";
+ }
+}
+
+
+sub Test_Master_Index {
+ my $mi = IO::File->new("Doc1/masterindex.html");
+ my @lines = <$mi>;
+ $mi->close;
+ my %seen = ();
+ my $ok = 1;
+
+ # All header types are in the source files,
+ # so they should also be listed in the master index.
+ # Lets test this.
+ foreach my $line (@lines) {
+ if ($line =~ m/top">([^<]+)<\/a>]/i) {
+ $seen{$1}++;
+ }
+ }
+ foreach my $file ("Modules", "Sourcefiles",
+ "Classes", "Structures", "Functions",
+ "Variables", "Procedures",
+ "Types", "Exceptions", "Definitions" ) {
+ $ok = 0 unless ( defined($seen{$file}) and $seen{$file} == 1 );
+ }
+ # Test if all source files are listed.
+# foreach my $line (@lines) {
+# if (($line =~ m/Source\sFiles/) ..
+# ($line =~ m/\/table/i)) {
+# if ($line =~ m/([a-z][a-z]file\.dat)/) {
+# $seen{$1}++;
+# }
+# }
+# }
+# foreach my $file ( qw( ccfile.dat bbfile.dat aafile.dat) ) {
+# $ok = 0 unless ( defined($seen{$file}) and $seen{$file} == 1 );
+# }
+# # Test table for /****d*
+# foreach my $line (@lines) {
+# if (($line =~ m/Constants/) ..
+# ($line =~ m/\/table/i)) {
+# if ($line =~ m/_test_(\S+)_test_/) {
+# $seen{$1}++;
+# }
+# }
+# }
+# foreach my $name ( qw( def1 def2 ) ) {
+# $ok = 0 unless ( defined($seen{$name}) and $seen{$name} == 1 );
+# }
+# # Test table for /****f*
+# foreach my $line (@lines) {
+# if (($line =~ m/Functions/) ..
+# ($line =~ m/\/table/i)) {
+# if ($line =~ m/_test_(\S+)_test_/) {
+# $seen{$1}++;
+# }
+# }
+# }
+# foreach my $name ( qw( fun1 fun2 ) ) {
+# $ok = 0 unless ( defined($seen{$name}) and $seen{$name} == 1 );
+# }
+# # Test table for /****c*
+# foreach my $line (@lines) {
+# if (($line =~ m/Classes/) ..
+# ($line =~ m/\/table/i)) {
+# if ($line =~ m/_test_(\S+)_test_/) {
+# $seen{$1}++;
+# }
+# }
+# }
+# foreach my $name ( qw( class1 class2 class3 ) ) {
+# $ok = 0 unless ( defined($seen{$name}) and $seen{$name} == 1 );
+# }
+ return $ok;
+}
+
+#
+# This tests the TOC of aafile.dat
+#
+#
+sub Test_aafile_dat {
+ my $mi = IO::File->new("Doc1/aafile_dat.html");
+ my @lines = <$mi>;
+ $mi->close;
+ my %seen = ();
+ my $ok = 1;
+ # Scan the table of content
+ foreach my $line (@lines) {
+ if (($line =~ m/TABLE\sOF\sCONTENTS/i) ..
+ ($line =~ m/\/UL/i)) {
+ if ($line =~ m/\/([ABCD][ab][ab]__level\d__)/) {
+ $seen{$1}++;
+ }
+ }
+ }
+ foreach my $name ( qw( Aaa__level1__ Bbb__level2__ Abb__level3__ Bbb__level3__ Cbb__level3__ Dbb__level3__ ) ) {
+ $ok = 0 unless ( defined($seen{$name}) and $seen{$name} == 1 );
+ }
+
+ return $ok;
+}
+
+ROBOTest::assertFile("Doc1/masterindex.html");
+ROBOTest::assertDir("Doc1/Subdir");
+ROBOTest::assertFile("Doc1/Subdir/ccfile_dat.html");
+ROBOTest::assertFile("Doc1/aafile_dat.html");
+ROBOTest::assertFile("Doc1/bbfile_dat.html");
+ROBOTest::assert( Test_Master_Index(), "Master index content" );
+ROBOTest::assert( Test_aafile_dat(), "aafile TOC" );
+
+ROBOTest::finish;
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: nosort_test.pl,v 1.2 2004/06/20 09:24:36 gumpu Exp $
+#
+
+use strict;
+use warnings;
+use ROBOTest;
+use IO::File;
+
+#****x* SystemTest/nosort_test_pl
+# FUNCTION
+# ROBODoc sorts the TOC and the headers so that they appear in
+# a particular order in the documentation.
+# Here we test if the order is the order we expect.
+# This is done by generating the documentation and comparing
+# the order with a table. There are two source files, one
+# that is already sorted, and one that is unsorted.
+# The documentation for both should end up sorted.
+# SOURCE
+#
+
+
+ROBOTest::start("Sort Test");
+
+# in the documentation for the sorted file when
+# it is sorted the TOC should look like this.
+my @toc_sorted_names = qw(
+ AA_Header/BB_header
+ BB_header/aaa_function
+ BB_header/bbb_function
+ BB_header/ccc_function
+);
+
+my @toc_unsorted_names = qw(
+ XX_Header/ZZ_Header
+ XX_Header/AA_Header
+ ZZ_Header/DD_Header
+ AA_Header/CC_header
+ CC_header/ccc_function
+ DD_header/aaa_function
+ CC_header/aaa_function
+ CC_header/fff_function
+ CC_header/eee_function
+);
+
+
+sub check_toc_order {
+ my $ok = 1;
+ my $filename = shift; # filename of the file with the headers
+ my $order = shift; # order of the headers
+ my $number = shift; # number of headers
+ my $file = IO::File->new("<$filename") or die;
+ my $index = 0;
+ while( my $line = <$file> ) {
+ if ( $line =~ m/robo\d+">([^<]+)</ ) {
+ my $name = $1;
+ if ( $name eq ${$order}[ $index ] ) {
+ # All is OK.
+ } else {
+ $ok = 0;
+ }
+ ++$index;
+ last if ( $index == $number );
+ }
+ }
+ $file->close();
+
+ return $ok;
+}
+
+ROBOTest::assertFile("Doc1/sorted_c.html");
+ROBOTest::assert(
+ check_toc_order( "Doc1/sorted_c.html", \@toc_sorted_names, 4 ),
+ "Is the TOC still sorted"
+);
+
+ROBOTest::assertFile("Doc1/unsorted_c.html");
+ROBOTest::assert(
+ check_toc_order( "Doc1/unsorted_c.html", \@toc_unsorted_names, 4 ),
+ "Is the TOC now sorted"
+);
+
+ROBOTest::finish;
+
+#****
+
--- /dev/null
+/****f* Test/test
+ * NAME
+ * Test
+ * SOURCE
+ */
+
+should not appear;
+should not appear;
+should not appear;
+
+/****/
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Test whether robodoc can handle the --lock mode
+# In the lock mode it will lock on to one kind of header marker
+# per file and one kind of remark marker per header. All
+# other ones should be skipped.
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("--nosource Test");
+my $checksum = 0;
+
+while (my $line = <>) {
+ if ($line =~ m/should\snot\sappear/) {
+ ++$checksum;
+ }
+}
+ROBOTest::assert( $checksum == 0 );
+ROBOTest::finish;
+
--- /dev/null
+#****f* Test/test
+# NAME
+# Test
+#
+#*****
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Test the --rc option
+# If the works there should be a file called
+# option_rc_test.html
+#
+# $Id: option_rc_test.pl,v 1.1 2003/08/13 20:42:07 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Test of --rc");
+ROBOTest::assertFile( "option_rc_test.html" );
+ROBOTest::finish;
--- /dev/null
+/****h* ATest/Test1
+ * NAME
+ * Test2
+ * RESULT
+ * Test3
+ ******
+ */
+
+
+/****h* ATest/Test10
+ * NAME
+ * Test20
+ * RESULT
+ * Test30
+ ******
+ */
+
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Test whether robodoc can generate output in all the formats
+# it is supposed to support.
+#
+
+# TODO
+#
+use strict;
+
+print "Outputformat Test: ";
+
+print<<EOF;
+FAIL
+This test is not yet implemented.
+EOF
+
+
--- /dev/null
+
+#****f* Test/test
+# NAME
+# test
+#***
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Test of the various ways we can specify files
+# and directories to the --src and --doc options.
+# Created to solve bug:
+# [770251 ] Absolute filename causes an assertion to fail
+#
+#
+# $Id: parameter_test.pl,v 1.3 2003/08/13 20:42:07 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Parameter Test");
+ROBOTest::assertFile( "parameter_test.html" );
+ROBOTest::finish;
--- /dev/null
+/****m* pipe/pipetest
+ * NAME
+ * pipetest
+ * NAME
+ * Simple header to test "piping" features in items.
+ * EXAMPLE
+ * Only "pipes" which match selected output style are picked up.
+ * |html <CENTER>This will be included in <B>HTML</B> output.</CENTER>
+ * |latex \centerline{This will be included in \LaTeX output}
+ * Space is mandatory following the pipe marker. The following is not a
+ * valid pipe marker:
+ * |html<B>Moi!</B>
+ * You should see an equation on the following line:
+ * |html y = x^2 (sorry, plain HTML is not very powerfull)
+ * |latex \centerline{$y = x^2$}
+ * How does this look like?
+ * Here comes a multi-line equation array:
+ * |latex \begin{eqnarray}
+ * |latex \frac{\partial u}{\partial \tau} & = & D_u {\nabla}^2 u +
+ * |latex \frac{1}{\epsilon}
+ * |latex \left ( \hat{u}-{\hat{u}}^2-f\, {v} \, \frac{\hat{u}-q}{\hat{u}+q}
+ * |latex \right ) \; , \label{diffspot:1} \\
+ * |latex \frac{\partial v}{\partial \tau} & = & \hat{u}-v \; ,
+ * |latex \label{diffspot:2} \\
+ * |latex \frac{\partial r}{\partial \tau} & = & D_r {\nabla}^2 r \; .
+ * |latex \label{diffspAot:3}
+ * |latex \end{eqnarray}
+ * |html <I>TODO: write this in html</I>
+ * Remove this line and you'll experience a bug...
+ ******
+ */
--- /dev/null
+#!/usr/bin/perl -w
+# $Id: pipe_test.pl,v 1.1 2003/11/18 12:10:26 petterik Exp $
+
+use strict;
+use ROBOTest;
+
+my $n = $ARGV[0] || 1;
+my $expected = $ARGV[1] || 0;
+
+sub test1 {
+ my $res = 1;
+ while (my $line = <STDIN>) {
+ if ($line =~ m/<CENTER>This will be included in <B>HTML<\/B> output.<\/CENTER>/) {
+ $res = 0;
+ }
+ }
+ return $res;
+}
+
+sub test2 {
+ my $res = 1;
+ while (my $line = <STDIN>) {
+ if ($line =~ m/\\centerline{This will be included in \\LaTeX output}/) {
+ $res = 0;
+ }
+ }
+ return $res;
+}
+
+sub test3 {
+ my $res = 1;
+ while (my $line = <STDIN>) {
+ if ($line =~ /\|html<\;B>\;Moi!<\;\/B>\;/) {
+ $res = 0;
+ }
+ }
+ return $res;
+}
+
+my %tests = (
+ 1 => \&test1,
+ 2 => \&test2,
+ 3 => \&test3
+ );
+
+ROBOTest::start("Pipe Test $n-$expected");
+
+my $status = $tests{$n}();
+
+ROBOTest::assert( $status == $expected );
+ROBOTest::finish;
--- /dev/null
+
+See documentation in the makefile.
+
--- /dev/null
+/****f* Test/Test *
+ * NAME
+ * remark markers test
+ * LINE 1
+// LINE 2
+* LINE 3
+;* LINE 4
+; LINE 5
+C LINE 6
+REM LINE 7
+% LINE 8
+# LINE 9
+ * LINE 10
+-- LINE 11
+| LINE 12
+!! LINE 13
+ LINE 14 Should be skipped, because it does not start with a
+ remark marker
+# SOURCE
+
+ LINE 15 Should not be skipped because it is part of a source item
+# LINE 16 This too should be there
+/******/
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("RemarkMarker Test");
+
+#
+# The ,res should look like
+# LINE 1
+# LINE 2
+# LINE 3
+# etc
+# We extract all the numbers and store them
+# in a hash
+#
+my %count = ();
+while (my $line = <>) {
+ if ($line =~ m/\s*LINE\s+(\d+)\s/) {
+ $count{$1}++;
+ }
+}
+
+# there should be 15 different lines
+#
+my $number_of_lines = scalar(keys %count);
+
+ROBOTest::assert($number_of_lines == 15, "header types");
+ROBOTest::finish;
--- /dev/null
+#options:
+# --tell
+#
+ignore files:
+ CVS
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: robohdrs_test.pl,v 1.2 2003/12/26 22:31:29 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+ROBOTest::start("ROBOHDRS Test");
+ROBOTest::assertFile("Doc1/robo_functions.html");
+ROBOTest::assertFile("Doc1/robo_variables.html");
+ROBOTest::finish;
+
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: sort_test.pl,v 1.2 2004/06/20 09:24:36 gumpu Exp $
+#
+
+use strict;
+use warnings;
+use ROBOTest;
+use IO::File;
+
+#****x* SystemTest/sort_test_pl
+# FUNCTION
+# ROBODoc sorts the TOC and the headers so that they appear in
+# a particular order in the documentation.
+# Here we test if the order is the order we expect.
+# This is done by generating the documentation and comparing
+# the order with a table. There are two source files, one
+# that is already sorted, and one that is unsorted.
+# The documentation for both should end up sorted.
+# SOURCE
+#
+
+
+ROBOTest::start("Sort Test");
+
+# in the documentation for the sorted file when
+# it is sorted the TOC should look like this.
+my @toc_sorted_names = qw(
+ AA_Header/BB_header
+ BB_header/aaa_function
+ BB_header/bbb_function
+ BB_header/ccc_function
+);
+
+my @toc_unsorted_names = qw(
+ AA_Header/CC_header
+ XX_Header/AA_Header
+ XX_Header/ZZ_Header
+ ZZ_Header/DD_Header
+ CC_header/aaa_function
+ CC_header/ccc_function
+ CC_header/eee_function
+ CC_header/fff_function
+ DD_header/aaa_function
+);
+
+
+sub check_toc_order {
+ my $ok = 1;
+ my $filename = shift; # filename of the file with the headers
+ my $order = shift; # order of the headers
+ my $number = shift; # number of headers
+ my $file = IO::File->new("<$filename") or die;
+ my $index = 0;
+ while( my $line = <$file> ) {
+ if ( $line =~ m/robo\d">([^<]+)</ ) {
+ my $name = $1;
+ if ( $name eq ${$order}[ $index ] ) {
+ # All is OK.
+ } else {
+ $ok = 0;
+ }
+ ++$index;
+ last if ( $index == $number );
+ }
+ }
+ $file->close();
+
+ return $ok;
+}
+
+ROBOTest::assertFile("Doc1/sorted_c.html");
+ROBOTest::assert(
+ check_toc_order( "Doc1/sorted_c.html", \@toc_sorted_names, 4 ),
+ "Is the TOC still sorted"
+);
+
+ROBOTest::assertFile("Doc1/unsorted_c.html");
+ROBOTest::assert(
+ check_toc_order( "Doc1/unsorted_c.html", \@toc_unsorted_names, 4 ),
+ "Is the TOC now sorted"
+);
+
+ROBOTest::finish;
+
+#*****
--- /dev/null
+/****h* URLTest/URLTest
+ * NAME
+ * URLTest
+ * NOTES
+ * dummy
+ * XXX 1
+ * XXX 2
+ * XXX 3
+ * XXX 4
+ * XXX 5
+ *
+ *
+ *****
+ */
+
--- /dev/null
+#!/usr/bin/perl -w
+#
+#
+# $Id: tabtest.pl,v 1.3 2003/08/13 20:42:07 gumpu Exp $
+#
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("Tab Test");
+
+my $count = 0;
+while (my $line = <>) {
+ if ($line =~ m/^\s\s\s\s\s\sXXX/i) {
+ $count++;
+ }
+}
+
+ROBOTest::assert( $count == 5, "Testing tabsize of 4" );
+ROBOTest::finish;
+
--- /dev/null
+/****h* URLTest/URLTest
+ * NAME
+ * URLTest
+ * NOTES
+ * file:/my_file.testmark_1
+ * href:ftp://my_href.testmark_2
+ * http://my_http.testmark_3
+ * http://my_http.testmark_4.
+ * mailto:my_mail@testmark_5
+ * image:http://www.jinwicked.com/images/art/stallman.jpg
+ *****
+ */
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use ROBOTest;
+
+ROBOTest::start("URL Test");
+
+my $count = 0;
+while (my $line = <>) {
+ if (($line =~ m/<A\sHREF="file:\/my_file\.testmark_\d"/i) or
+ ($line =~ m/<A\sHREF="http:\/\/my_http\.testmark_\d"/i) or
+ ($line =~ m/<A\sHREF="ftp:\/\/my_href\.testmark_\d"/i) or
+ ($line =~ m/<A\sHREF="mailto:my_mail\@testmark_\d"/i) or
+ ($line =~ m/<img\ssrc="http:\/\/www\.jinwicked\.com\/images\/art\/stallman\.jpg">/i)
+ ) {
+ $count++;
+ }
+}
+
+# there should be 6
+#
+ROBOTest::assert($count == 6);
+ROBOTest::finish;
+
--- /dev/null
+# ROBO_UnitTest
+#
+body
+{
+ background-color: #ffffff;
+ color: #000000;
+ font-family: 'Lucida Grande', Verdana,
+ Geneva, Lucida, Arial,
+ Helvetica, sans-serif;
+ font-size: 10pt;
+ margin: 2% 5%;
+}
+h2
+{
+ background-color: #dddddd;
+ color: #000000;
+ text-align: right;
+ font-size: 11pt;
+}
+pre
+{
+ background-color: #ffffff;
+ color: #000000;
+ font-size: 10pt;
+}
+#
+# ROBO_UnitTest
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# This tests the option --css
+#
+#
+# $Id: user_specified_css_file_test.pl,v 1.2 2003/08/31 09:29:28 gumpu Exp $
+
+use strict;
+use ROBOTest;
+ROBOTest::start("Option --css Test");
+#
+#
+# The file user_css.css
+# contains twice the word ROBO_UnitTest
+# It should therefore also appear twice
+# in the robodoc.css file.
+#
+my $sum = 0;
+while (my $line = <>) {
+ if ( $line =~ m/ROBO_UnitTest/ ) {
+ $sum++;
+ }
+}
+
+ROBOTest::assert( $sum == 2 );
+ROBOTest::finish;
+
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+
+/****h* ROBODoc/Analyser
+ * NAME
+ * Analyser -- Functions to scan source and collect headers
+ * FUNCTION
+ * This module provides the functions to scan a sourcefile and
+ * collect all the headers.
+ *
+ *****
+ * $Id: analyser.c,v 1.73 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "robodoc.h"
+#include "globals.h"
+#include "headers.h"
+#include "headertypes.h"
+#include "items.h"
+#include "util.h"
+#include "links.h"
+#include "analyser.h"
+#include "document.h"
+#include "file.h"
+#include "part.h"
+#include "roboconfig.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+static int ToBeAdded(
+ struct RB_Document *document,
+ struct RB_header *header );
+static int Find_Header_Name(
+ FILE *,
+ struct RB_header * );
+static struct RB_header *Grab_Header(
+ FILE *sourcehandle,
+ struct RB_Document *arg_document );
+static char *Function_Name(
+ char *header_name );
+static char *Module_Name(
+ char *header_name );
+static int Find_End_Marker(
+ FILE *document,
+ struct RB_header *new_header );
+struct RB_HeaderType *AnalyseHeaderType(
+ char **cur_char,
+ int *is_internal );
+static struct RB_HeaderType *RB_Find_Marker(
+ FILE *document,
+ int *is_internal,
+ int reuse_previous_line );
+static int Analyse_Items(
+ struct RB_header *arg_header );
+static int Is_ListItem_Start(
+ char *arg_line,
+ int arg_indent );
+
+/****f* Analyser/Is_Pipe_Marker
+ * NAME
+ * Is_Pipe_Marker
+ * FUNCTION
+ * Check for "pipe" markers e.g. "|html ".
+ * SYNOPSIS
+ */
+static char *Is_Pipe_Marker(
+ char *cur_char,
+ int *pipe_mode )
+/*
+ * RESULT
+ * Pointer to the data to be piped to document or in case no pointers
+ * are found.
+ * SEE ALSO
+ * RB_Check_Pipe
+ * SOURCE
+ */
+{
+ char *s = cur_char + 1;
+
+ *pipe_mode = -1;
+ if ( *cur_char == '|' && *s )
+ {
+ if ( strncmp( "html ", s, 5 ) == 0 )
+ {
+ *pipe_mode = HTML;
+ return ( s + 5 );
+ }
+ else if ( strncmp( "latex ", s, 6 ) == 0 )
+ {
+ *pipe_mode = LATEX;
+ return ( s + 6 );
+ }
+ else if ( strncmp( "rtf ", s, 4 ) == 0 )
+ {
+ *pipe_mode = RTF;
+ return ( s + 4 );
+ }
+ else if ( strncmp( "dbxml ", s, 6 ) == 0 )
+ {
+ *pipe_mode = XMLDOCBOOK;
+ return ( s + 6 );
+ }
+ else if ( strncmp( "ascii ", s, 6 ) == 0 )
+ {
+ *pipe_mode = ASCII;
+ return ( s + 6 );
+ }
+ }
+
+ return ( char * ) NULL;
+}
+
+/*****/
+
+
+/****f* Analyser/Is_Tool
+ * SYNOPSIS
+ */
+static char *Is_Tool(
+ char *cur_char,
+ enum ItemLineKind *itemkind,
+ int *tool_active )
+/*
+ * FUNCTION
+ * Checks for tool start and end markers
+ * SOURCE
+ */
+{
+ char *s = cur_char + 1;
+
+ if ( *cur_char == '|' && *s )
+ {
+ // Check if tool starts or ends
+ if ( !strncmp( "tool ", s, 5 ) )
+ {
+ if ( *tool_active )
+ {
+ *itemkind = ITEM_LINE_TOOL_END;
+ *tool_active = 0;
+ }
+ else
+ {
+ *itemkind = ITEM_LINE_TOOL_START;
+ *tool_active = 1;
+ }
+
+ return ( s + 5 );
+ }
+ // Check if DOT starts or ends
+ if ( !strncmp( "dot ", s, 4 ) )
+ {
+ if ( *tool_active )
+ {
+ *itemkind = ITEM_LINE_DOT_END;
+ *tool_active = 0;
+ }
+ else
+ {
+ *itemkind = ITEM_LINE_DOT_START;
+ *tool_active = 1;
+ }
+
+ return ( s + 4 );
+ }
+ // Check for DOT file includes
+ else if ( !strncmp( "dotfile ", s, 8 ) && !*tool_active )
+ {
+ *itemkind = ITEM_LINE_DOT_FILE;
+ return ( s + 8 );
+ }
+ // Check for exec items
+ else if ( !strncmp( "exec ", s, 5 ) && !*tool_active )
+ {
+ *itemkind = ITEM_LINE_EXEC;
+ return ( s + 5 );
+ }
+ }
+
+ return NULL;
+}
+
+/*****/
+
+/****f* Analyser/RB_Analyse_Document
+ * foo
+ * FUNCTION
+ * Scan all the sourcefiles of all parts of a document for
+ * headers. Store these headers in each part (RB_Part).
+ * SYNOPSIS
+ */
+void RB_Analyse_Document(
+ struct RB_Document *arg_document )
+/*
+ * INPUTS
+ * o document -- document to be analysed.
+ * RESULT
+ * Each part will contain the headers that were found in the
+ * sourcefile of the part.
+ * SOURCE
+ */
+{
+ struct RB_Part *a_part;
+ struct RB_Filename *a_filename;
+ FILE *filehandle;
+
+ for ( a_part = arg_document->parts; a_part; a_part = a_part->next )
+ {
+ struct RB_header *new_header = NULL;
+
+ a_filename = a_part->filename;
+ RB_Say( "analysing %s\n", SAY_DEBUG, Get_Fullname( a_filename ) );
+ RB_SetCurrentFile( Get_Fullname( a_filename ) );
+
+ RB_Header_Lock_Reset( );
+ filehandle = RB_Open_Source( a_part );
+ line_number = 0;
+
+ for ( new_header = Grab_Header( filehandle, arg_document );
+ new_header;
+ new_header = Grab_Header( filehandle, arg_document ) )
+ {
+ if ( ToBeAdded( arg_document, new_header ) )
+ {
+ /* The Add is required before the
+ * Analyse because Add sets the owner of the header
+ * which is needed for error messages.
+ */
+ RB_Part_Add_Header( a_part, new_header );
+ Analyse_Items( new_header );
+ }
+ else
+ {
+ RB_Free_Header( new_header );
+ }
+ }
+ fclose( filehandle );
+ }
+}
+
+/*****/
+
+#if 0
+/****f* Analyser/Check_Header_Start
+ * FUNCION
+ * Sometimes a user makes a spelling mistake in the name of the first item.
+ * ROBODoc then ignores all the lines leading up to the second item,
+ * (which is the first item ROBODoc recognized). This function
+ * checks for this condition and gives the user a warning.
+ * SYNOPSIS
+ */
+static void Check_Header_Start(
+ struct RB_header *arg_header,
+ int first_item )
+/*
+ * INPUTS
+ * * arg_header -- the header to be checked.
+ * * first -- the line on which the first item was found
+ * RESULT
+ * A warning is given if the condition occured.
+ * SOURCE
+ */
+{
+ if ( first_item )
+ {
+ int i = 0;
+
+ for ( i = 0; i < first_item; ++i )
+ {
+ char *c = arg_header->lines[i];
+
+ c = RB_Skip_Whitespace( c );
+ if ( RB_Has_Remark_Marker( c ) )
+ {
+ c = RB_Skip_Remark_Marker( c );
+ for ( ; *c; ++c )
+ {
+ if ( !utf8_isspace( *c ) )
+ {
+ RB_Warning_Full( Get_Fullname
+ ( arg_header->owner->filename ),
+ arg_header->line_number,
+ "Header \"%s\" contains text before the fist item, "
+ "this might be caused by a misspelled item name.\n",
+ arg_header->name );
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*******/
+#endif
+
+/****f* Analyser/Is_Empty_Line
+ * FUNCTION
+ * Check if line is empty. This assumes that
+ * Copy_Lines_To_Item has been run on the item.
+ * SYNOPSIS
+ */
+static int Is_Empty_Line(
+ char *line )
+/*
+ * INPUTS
+ * * line -- the string to be analysed.
+ * SOURCE
+ */
+{
+ line = RB_Skip_Whitespace( line );
+ return ( *line == '\0' );
+}
+
+/******/
+
+
+/****f* Analyser/Get_Indent
+ * FUNCION
+ * Determine the indentation of a line by counting
+ * the number of spaces at the begining of the line.
+ * SYNOPSIS
+ */
+static int Get_Indent(
+ char *line )
+/*
+ * INPUTS
+ * * line -- the line
+ * RESULT
+ * The indentation.
+ * SOURCE
+ */
+{
+ int i;
+
+ for ( i = 0; line[i] && utf8_isspace( line[i] ); ++i )
+ {
+ /* empty */
+ }
+ return i;
+}
+
+/*****/
+
+
+/****f* Analyser/Is_ListItem_Start
+ * FUNCTION
+ * Test if a line starts with something that indicates a list item.
+ * List items start with '*', '-', or 'o'.
+ * SYNPOPSIS
+ */
+static int Is_ListItem_Start(
+ char *arg_line,
+ int arg_indent )
+/*
+ * INPUTS
+ * * line -- the line
+ * RESULT
+ * * TRUE -- it did start with one of those characters
+ * * FALSE -- it did not.
+ * SOURCE
+ */
+{
+ char *c = arg_line;
+ int cur_indent = Get_Indent( arg_line );
+
+ if ( cur_indent == arg_indent )
+ {
+ /* TODO Think there is a function for this */
+ for ( ; *c && utf8_isspace( *c ); ++c )
+ { /* empty */
+ };
+
+ if ( *c && ( strlen( c ) >= 3 ) )
+ {
+ if ( strchr( "*-o", *c ) && utf8_isspace( *( c + 1 ) ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ else
+ {
+ /* The line is indented so it must be
+ * the start of a pre block */
+ }
+
+ return FALSE;
+}
+
+/*****/
+
+
+/****f* Analyser/Is_List_Item_Continuation
+ * FUNCTION
+ * Is it like the second line in something like:
+ * * this is a list item
+ * that continues
+ * SYNPOPSIS
+ */
+static int Is_List_Item_Continuation(
+ char *arg_line,
+ int indent )
+/*
+ * INPUTS
+ * * arg_line -- the current line
+ * * indent -- the indent of the current item.
+ * RESULT
+ * * TRUE -- it is.
+ * * FALSE -- it is not.
+ * SOURCE
+ */
+{
+ int this_indent = Get_Indent( arg_line );
+
+ return ( this_indent > indent );
+}
+
+/*****/
+
+
+#if 0
+/****f* Analyser/Is_End_of_List
+ * FUNCTION
+ * Check... (TODO)
+ * INPUTS
+ * * arg_line --
+ * * indent --
+ * SOURCE
+ */
+
+static int Is_End_of_List(
+ char *arg_line,
+ int indent )
+{
+ int this_indent = Get_Indent( arg_line );
+
+ return ( this_indent <= indent );
+}
+
+/******/
+#endif
+
+
+/****f* Analyser/Is_Start_List
+ * FUNCTION
+ * Check... (TODO)
+ * INPUTS
+ * * arg_line --
+ * * indent --
+ * SOURCE
+ */
+
+static int Is_Start_List(
+ char *arg_line,
+ int indent )
+{
+ int this_indent = Get_Indent( arg_line );
+ char *c = strrchr( arg_line, ':' );
+
+ if ( ( this_indent == indent ) && c )
+ {
+ for ( ++c; *c; ++c )
+ {
+ if ( !utf8_isspace( *c ) )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*******/
+
+
+/****f* Analyser/Remove_List_Char
+ * FUNCTION
+ * Remove... (TODO)
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * * start_index --
+ * SOURCE
+ */
+
+static void Remove_List_Char(
+ struct RB_Item *arg_item,
+ int start_index )
+{
+ char *c = arg_item->lines[start_index]->line;
+
+ for ( ; *c && utf8_isspace( *c ); ++c )
+ { /* empty */
+ };
+ if ( *c && ( strlen( c ) >= 3 ) )
+ {
+ if ( strchr( "*-o", *c ) && utf8_isspace( *( c + 1 ) ) )
+ {
+ char *temp = arg_item->lines[start_index]->line;
+
+ *c = ' ';
+ arg_item->lines[start_index]->line = RB_StrDup( temp + 2 );
+ free( temp );
+ }
+ }
+}
+
+/*******/
+
+/****f* Analyser/Analyse_ListBody
+ * FUNCTION
+ * Analyse... (TODO)
+ * SYNPOPSIS
+ */
+static int Analyse_ListBody(
+ struct RB_Item *arg_item,
+ int start_index,
+ int arg_indent )
+/*
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * * start_index --
+ * * arg_index --
+ * SOURCE
+ */
+{
+ int i = start_index;
+
+ for ( ; i < arg_item->no_lines; i++ )
+ {
+ char *line = arg_item->lines[i]->line;
+
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) ||
+ ( arg_item->lines[i]->kind == ITEM_LINE_END ) )
+ {
+ if ( Is_ListItem_Start( line, arg_indent ) )
+ {
+ arg_item->lines[i]->format |= RBILA_END_LIST_ITEM;
+ arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
+ Remove_List_Char( arg_item, i );
+ }
+ else if ( Is_List_Item_Continuation( line, arg_indent ) )
+ {
+ /* Nothing */
+ }
+ else
+ {
+ /* Must be the end of the list */
+ arg_item->lines[i]->format |= RBILA_END_LIST_ITEM;
+ arg_item->lines[i]->format |= RBILA_END_LIST;
+ break;
+ }
+ }
+ }
+ return i;
+}
+
+/*******/
+
+/****f* Analyser/Analyse_List
+ * FUNCTION
+ * Parse the item text to see if there are any lists.
+ * A list is either case I:
+ * ITEMNAME
+ * o bla bla
+ * o bla bla
+ * or case II:
+ * some text: <-- begin of a list
+ * o bla bla <-- list item
+ * bla bla bla <-- continuation of list item.
+ * o bla bla <-- list item
+ * <-- end of a list
+ * bla bla <-- this can also be the end of a list.
+ * SYNPOPSIS
+ */
+static void Analyse_List(
+ struct RB_Item *arg_item,
+ int indent )
+/*
+ * INPUTS
+ * * arg_item -- the item to be parsed.
+ * * indent -- the indent of this item.
+ * OUTPUT
+ * * arg_item -- the itemlines will contain formatting information
+ * for all the lists that were found.
+ * SOURCE
+ */
+{
+ if ( arg_item->no_lines >= 1 )
+ {
+ int i = 0;
+ char *line = arg_item->lines[i]->line;
+
+ /* Case I */
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
+ Is_ListItem_Start( line, indent ) )
+ {
+ /* Case I, the is a list item right after the item name */
+ arg_item->lines[i]->format |= RBILA_BEGIN_LIST;
+ arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
+ Remove_List_Char( arg_item, i );
+ /* Now try to find the end of the list */
+ i = Analyse_ListBody( arg_item, 1, indent );
+ }
+
+ /* Now search for case II cases */
+ for ( ; i < arg_item->no_lines; i++ )
+ {
+ line = arg_item->lines[i]->line;
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
+ Is_Start_List( line, indent ) )
+ {
+ ++i;
+ if ( i < arg_item->no_lines )
+ {
+ line = arg_item->lines[i]->line;
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
+ Is_ListItem_Start( line, indent ) )
+ {
+ arg_item->lines[i]->format |= RBILA_BEGIN_LIST;
+ arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
+ Remove_List_Char( arg_item, i );
+ ++i;
+ i = Analyse_ListBody( arg_item, i, indent );
+
+
+ /* One list might be immediately followed
+ * by another. In this case we have to
+ * analyse the last line again. */
+ line = arg_item->lines[i]->line;
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN )
+ && Is_Start_List( line, indent ) )
+ {
+ --i;
+ }
+
+ }
+ }
+ }
+ }
+ }
+}
+
+/******/
+
+// If (unused(this_function)) { delete(this_function) }
+#if 0
+/****f* Analyser/Dump_Item
+ * FUNCTION
+ * Print debug information.
+ */
+static void Dump_Item(
+ struct RB_Item *arg_item )
+/*
+ * INPUTS
+ * * arg_item -- the item to be pretty-printed.
+ * SOURCE
+ */
+{
+ int i;
+
+ /* preformatted blocks */
+ for ( i = 0; i < arg_item->no_lines; i++ )
+ {
+ char *line = arg_item->lines[i]->line;
+ int format = arg_item->lines[i]->format;
+
+ printf( "%04d ", i );
+ printf( ( format & RBILA_END_PARAGRAPH ) ? "p" : " " );
+ printf( ( format & RBILA_BEGIN_PARAGRAPH ) ? "P" : " " );
+ printf( ( format & RBILA_END_PRE ) ? "e" : " " );
+ printf( ( format & RBILA_BEGIN_PRE ) ? "E" : " " );
+ printf( ( format & RBILA_END_LIST ) ? "l" : " " );
+ printf( ( format & RBILA_BEGIN_LIST ) ? "L" : " " );
+ printf( ( format & RBILA_END_LIST_ITEM ) ? "i" : " " );
+ printf( ( format & RBILA_BEGIN_LIST_ITEM ) ? "I" : " " );
+ printf( " (%s)\n", line );
+ }
+}
+
+/*******/
+#endif
+
+/****f* Analyser/Preformat_All
+ * FUNCTION
+ * Process... (TODO)
+ * SYNPOPSIS
+ */
+static void Preformat_All(
+ struct RB_Item *arg_item,
+ int source )
+/*
+ * INPUTS
+ * * arg_item -- the item to be pre-formatted.
+ * * source -- is it a source item ?
+ * SOURCE
+ */
+{
+ int i;
+ int preformatted = FALSE;
+ char *line = NULL;
+
+ if ( arg_item->no_lines > 0 )
+ {
+ i = 0;
+ /* Skip any pipe stuff */
+ for ( ;
+ ( i < arg_item->no_lines )
+ && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
+ {
+ /* Empty */
+ }
+
+ line = arg_item->lines[i]->line;
+ if ( ( arg_item->lines[i]->kind == ITEM_LINE_RAW ) ||
+ ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) )
+ {
+ arg_item->lines[i]->format |=
+ RBILA_BEGIN_PRE | ( source ? RBILA_BEGIN_SOURCE : 0 );
+ preformatted = TRUE;
+
+ for ( ++i; i < arg_item->no_lines; i++ )
+ {
+ if ( arg_item->lines[i]->kind == ITEM_LINE_PIPE )
+ {
+ /* Temporarily end the preformatting to allow
+ * the piping to happen
+ */
+ arg_item->lines[i]->format |=
+ RBILA_END_PRE | ( source ? RBILA_END_SOURCE : 0 );
+ /* Find the end of the pipe stuff */
+ for ( ; ( i < arg_item->no_lines ) &&
+ ( arg_item->lines[i]->kind == ITEM_LINE_PIPE );
+ ++i )
+ { /* Empty */
+ };
+ /* Every item ends with an ITEM_LINE_END, so: */
+ assert( i < arg_item->no_lines );
+ /* And re-enable preformatting */
+ arg_item->lines[i]->format |=
+ RBILA_BEGIN_PRE | ( source ? RBILA_BEGIN_SOURCE : 0 );
+ }
+
+ if ( arg_item->lines[i]->kind == ITEM_LINE_END )
+ {
+ /* If the last line ends with a begin_pre remove
+ * it, otherwise a begin and end pre will be
+ * generated, in the wrong order, on the same line in the output.
+ */
+ if ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) {
+ arg_item->lines[i]->format &= ~( RBILA_BEGIN_PRE );
+ } else {
+ arg_item->lines[i]->format |= RBILA_END_PRE;
+ }
+ arg_item->lines[i]->format |= ( source ? RBILA_END_SOURCE : 0 );
+ }
+ }
+ }
+ }
+}
+
+/******/
+
+/*
+ * aaaaa
+ * aa
+ * </p>
+ * aa <p>
+ * aaa
+ * aaaa
+ *
+ *
+ */
+
+/****f* Analyser/Analyse_Preformatted
+ * FUNCTION
+ * Analyse preformatted text ... (TODO)
+ * SYNPOPSIS
+ */
+static void Analyse_Preformatted(
+ struct RB_Item *arg_item,
+ int indent )
+/*
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * * indent --
+ * SOURCE
+ */
+{
+ int i;
+ int in_list = FALSE;
+ int new_indent = -1;
+ int preformatted = FALSE;
+ char *line = NULL;
+
+ /* preformatted blocks */
+ if ( arg_item->no_lines > 0 )
+ {
+ i = 0;
+ /* Skip any pipe stuff */
+ for ( ;
+ ( i < arg_item->no_lines )
+ && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
+ {
+ /* Empty */
+ }
+
+ line = arg_item->lines[i]->line;
+
+ if ( ( !in_list )
+ && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) )
+ {
+ in_list = TRUE;
+ }
+ if ( ( in_list ) && ( arg_item->lines[i]->format & RBILA_END_LIST ) )
+ {
+ in_list = FALSE;
+ }
+
+ for ( ++i; i < arg_item->no_lines; i++ )
+ {
+ if ( arg_item->lines[i]->kind == ITEM_LINE_PIPE )
+ {
+ if ( preformatted )
+ {
+ arg_item->lines[i]->format |= RBILA_END_PRE;
+ }
+ for ( ; ( i < arg_item->no_lines ) &&
+ ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
+ { /* Empty */
+ };
+ /* Every item ends with an ITEM_LINE_END, so: */
+ assert( i < arg_item->no_lines );
+ if ( preformatted )
+ {
+ arg_item->lines[i]->format |= RBILA_BEGIN_PRE;
+ }
+ }
+
+ line = arg_item->lines[i]->line;
+ new_indent = Get_Indent( line );
+
+ if ( ( !in_list )
+ && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) )
+ {
+ in_list = TRUE;
+ }
+ if ( ( in_list )
+ && ( arg_item->lines[i]->format & RBILA_END_LIST ) )
+ {
+ in_list = FALSE;
+ }
+
+ if ( !in_list )
+ {
+ if ( ( new_indent > indent ) && !preformatted )
+ {
+ preformatted = TRUE;
+ arg_item->lines[i]->format |= RBILA_BEGIN_PRE;
+ }
+ else if ( ( new_indent <= indent ) && preformatted )
+ {
+ preformatted = FALSE;
+ arg_item->lines[i]->format |= RBILA_END_PRE;
+ }
+ else
+ {
+ /* An empty line */
+ }
+ }
+ else
+ {
+ /* We are in a list, do nothing */
+ }
+ }
+ }
+}
+
+/******/
+
+/****f* Analyser/Analyse_Paragraphs
+ * FUNCTION
+ * Analyse paragraphs... (TODO)
+ * SYNPOPSIS
+ */
+static void Analyse_Paragraphs(
+ struct RB_Item *arg_item )
+/*
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * SOURCE
+ */
+{
+ int i;
+ int in_par = FALSE;
+ int in_list = FALSE;
+ int in_pre = FALSE;
+ int is_empty = FALSE;
+ int prev_is_empty = FALSE;
+
+ for ( i = 0;
+ ( i < arg_item->no_lines )
+ && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
+ {
+ /* Empty */
+ }
+ assert( i < arg_item->no_lines );
+
+ if ( ( arg_item->lines[i]->format == 0 ) )
+ {
+ arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH;
+ in_par = TRUE;
+ }
+ for ( ; i < arg_item->no_lines; i++ )
+ {
+ char *line = arg_item->lines[i]->line;
+
+ prev_is_empty = is_empty;
+ is_empty = Is_Empty_Line( line );
+ if ( arg_item->lines[i]->format & RBILA_BEGIN_LIST )
+ {
+ in_list = TRUE;
+ }
+ if ( arg_item->lines[i]->format & RBILA_BEGIN_PRE )
+ {
+ in_pre = TRUE;
+ }
+ if ( arg_item->lines[i]->format & RBILA_END_LIST )
+ {
+ in_list = FALSE;
+ }
+ if ( arg_item->lines[i]->format & RBILA_END_PRE )
+ {
+ in_pre = FALSE;
+ }
+ if ( in_par )
+ {
+ if ( ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) ||
+ ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) ||
+ is_empty )
+ {
+ in_par = FALSE;
+ arg_item->lines[i]->format |= RBILA_END_PARAGRAPH;
+ }
+ }
+ else
+ {
+ if ( ( arg_item->lines[i]->format & RBILA_END_LIST ) ||
+ ( arg_item->lines[i]->format & RBILA_END_PRE ) ||
+ ( !is_empty && prev_is_empty && !in_list && !in_pre ) )
+ {
+ in_par = TRUE;
+ arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH;
+ }
+ }
+ }
+ if ( in_par )
+ {
+ arg_item->lines[arg_item->no_lines - 1]->format |=
+ RBILA_END_PARAGRAPH;
+ }
+}
+
+/******/
+
+
+/****f* Analyser/Analyse_Indentation
+ * FUNCTION
+ * Figure out how text is indented.
+ * SYNPOPSIS
+ */
+static int Analyse_Indentation(
+ struct RB_Item *arg_item )
+/*
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * SOURCE
+ */
+{
+ int i;
+ int indent = -1;
+
+ assert( arg_item->no_lines > 0 );
+
+ for ( i = 0; i < arg_item->no_lines; ++i )
+ {
+ if ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN )
+ {
+ char *line = arg_item->lines[i]->line;
+
+ if ( Is_Empty_Line( line ) )
+ {
+ /* Empty */
+ indent = 0;
+ }
+ else
+ {
+ indent = Get_Indent( line );
+ break;
+ }
+ }
+ }
+ assert( indent >= 0 );
+ return indent;
+}
+
+/******/
+
+/****f* Analyser/Analyse_Item_Format
+ * FUNCTION
+ * Try to determine the formatting of an item.
+ * An empty line generates a new paragraph
+ * Things that are indented more that the rest of the text
+ * are preformatted.
+ * Things that start with a '*' or '-' create list items
+ * unless they are indented more that the rest of the text.
+ * SYNPOPSIS
+ */
+static void Analyse_Item_Format(
+ struct RB_Item *arg_item )
+/*
+ * INPUTS
+ * * arg_item -- the item to be analysed.
+ * SOURCE
+ */
+{
+ // If item is not empty
+ if ( arg_item->no_lines )
+ {
+ // If it is a SOURCE item
+ if ( Works_Like_SourceItem( arg_item->type ) )
+ {
+ // Preformat it like a SOURCE item
+ Preformat_All( arg_item, TRUE );
+ }
+ // Check if we have to analyse this item
+ else if ( ( course_of_action.do_nopre
+ || Is_Format_Item( arg_item->type ) )
+ && !Is_Preformatted_Item( arg_item->type ) )
+ {
+ // analyse item
+ int indent = Analyse_Indentation( arg_item );
+
+ Analyse_List( arg_item, indent );
+ Analyse_Preformatted( arg_item, indent );
+ Analyse_Paragraphs( arg_item );
+ }
+ // If none of above, preformat item
+ else
+ {
+ // Preformat it
+ Preformat_All( arg_item, FALSE );
+ }
+ }
+ // Item is empty
+ else
+ {
+ // Do nothing
+ }
+}
+
+/*****/
+
+
+
+static int Trim_Empty_Item_Begin_Lines(
+ struct RB_header *arg_header,
+ struct RB_Item *arg_item,
+ int current_index )
+{
+
+ char *c;
+
+ if ( Works_Like_SourceItem( arg_item->type ) )
+ {
+ /* We skip the first line after the source item, if
+ * it an remark end marker -- such as '*)'
+ */
+ c = arg_header->lines[current_index];
+ if ( RB_Is_Remark_End_Marker( c ) )
+ {
+ c = RB_Skip_Remark_End_Marker( c );
+ c = RB_Skip_Whitespace( c );
+ if ( *c != '\0' )
+ {
+ c = arg_header->lines[current_index];
+ RB_Warning( "text following a remark end marker:\n%s\n", c );
+ }
+ ++current_index;
+ }
+ }
+
+ if ( current_index > arg_item->end_index )
+ {
+ /* item is empty */
+ }
+ else
+ {
+ do
+ {
+ c = arg_header->lines[current_index];
+ c = RB_Skip_Whitespace( c );
+ if ( RB_Has_Remark_Marker( c ) )
+ {
+ c = RB_Skip_Remark_Marker( c );
+ }
+ c = RB_Skip_Whitespace( c );
+ if ( *c == '\0' )
+ {
+ ++current_index;
+ }
+ }
+ while ( ( *c == '\0' ) && ( current_index < arg_item->end_index ) );
+ }
+
+ return current_index;
+}
+
+
+
+static int Trim_Empty_Item_End_Lines(
+ struct RB_header *arg_header,
+ struct RB_Item *arg_item,
+ int current_index )
+{
+ char *c;
+
+ if ( Works_Like_SourceItem( arg_item->type ) )
+ {
+ c = arg_header->lines[current_index];
+ if ( RB_Is_Remark_Begin_Marker( c ) )
+ {
+ c = RB_Skip_Remark_Begin_Marker( c );
+ c = RB_Skip_Whitespace( c );
+ if ( *c != '\0' )
+ {
+ c = arg_header->lines[current_index];
+ RB_Warning( "text following a remark begin marker:\n%s\n",
+ c );
+ }
+ --current_index;
+ }
+ }
+
+ do
+ {
+ c = arg_header->lines[current_index];
+ c = RB_Skip_Whitespace( c );
+ if ( RB_Has_Remark_Marker( c ) )
+ {
+ c = RB_Skip_Remark_Marker( c );
+ }
+ c = RB_Skip_Whitespace( c );
+ if ( *c == '\0' )
+ {
+ --current_index;
+ }
+ }
+ while ( ( *c == '\0' ) && ( current_index > arg_item->begin_index ) );
+
+ return current_index;
+}
+
+
+
+static void Trim_Empty_Item_Lines(
+ struct RB_header *arg_header,
+ struct RB_Item *arg_item )
+{
+ arg_item->no_lines = arg_item->end_index - arg_item->begin_index + 1;
+ if ( arg_item->no_lines <= 1 )
+ {
+ /* item is empty */
+ arg_item->no_lines = 0;
+ }
+ else
+ {
+ int current_index;
+
+ /* trim all empty lines at the begin of an item */
+
+ /* we skip the first line because that contains the item name.
+ */
+ current_index = arg_item->begin_index + 1;
+ current_index =
+ Trim_Empty_Item_Begin_Lines( arg_header, arg_item,
+ current_index );
+
+ /* Is there anything left? */
+ if ( current_index <= arg_item->end_index )
+ {
+ arg_item->begin_index = current_index;
+
+ /* trim all the empty lines at the end of an item */
+ current_index = arg_item->end_index;
+ current_index =
+ Trim_Empty_Item_End_Lines( arg_header, arg_item,
+ current_index );
+ if ( current_index >= arg_item->begin_index )
+ {
+ arg_item->end_index = current_index;
+ arg_item->no_lines =
+ arg_item->end_index - arg_item->begin_index + 1;
+ }
+ else
+ {
+ /* item is empty */
+ arg_item->no_lines = 0;
+ }
+ }
+ else
+ {
+ /* item is empty */
+ arg_item->no_lines = 0;
+ }
+ }
+}
+
+
+
+
+/* TODO This routine is way too long */
+
+static void Copy_Lines_To_Item(
+ struct RB_header *arg_header,
+ struct RB_Item *arg_item )
+{
+#if 0
+ printf( "%d\n%d\n%s\n%s\n",
+ arg_item->begin_index,
+ arg_item->end_index,
+ arg_header->lines[arg_item->begin_index],
+ arg_header->lines[arg_item->end_index] );
+#endif
+
+ Trim_Empty_Item_Lines( arg_header, arg_item );
+
+ if ( arg_item->no_lines > 0 )
+ {
+ int i = 0;
+ int j = 0;
+ struct RB_Item_Line *itemline = NULL;
+ int tool_active = 0; // Shows wether we are inside a tool body
+
+ /* Allocate enough memory for all the lines, plus one
+ * extra line
+ */
+ ++arg_item->no_lines;
+ arg_item->lines =
+ calloc( arg_item->no_lines, sizeof( struct RB_Item_Line * ) );
+ if ( !arg_item->lines )
+ {
+ RB_Panic( "Out of memory! %s\n", "Copy_Lines_To_Item" );
+ }
+
+ /* And create an RB_Item_Line for each of them, and add
+ * those to the RB_Item
+ */
+ for ( i = 0; i < arg_item->no_lines - 1; ++i )
+ {
+ char *c =
+ arg_header->lines[arg_item->begin_index + i];
+ /* TODO should be a Create_ItemLine() */
+ itemline = malloc( sizeof( struct RB_Item_Line ) );
+ if ( !itemline )
+ {
+ RB_Panic( "Out of memory! %s (2)\n", "Copy_Lines_To_Item" );
+ }
+
+ c = ExpandTab( c );
+ c = RB_Skip_Whitespace( c );
+ // Lines with remark marker
+ if ( RB_Has_Remark_Marker( c )
+ && !Works_Like_SourceItem( arg_item->type ) )
+ {
+ char *c2, *c3;
+ int pipe_mode;
+ enum ItemLineKind item_kind;
+
+ c = RB_Skip_Remark_Marker( c );
+ c2 = RB_Skip_Whitespace( c );
+ if ( *c2 )
+ {
+ // Check wether a tool starts or ends
+ if ( ( c3 = Is_Tool( c2, &item_kind, &tool_active ) ) )
+ {
+ itemline->kind = item_kind;
+ c = c3;
+ }
+ // If we have an active tool, copy the body lines
+ else if ( tool_active )
+ {
+ itemline->kind = ITEM_LINE_TOOL_BODY;
+ c++; // Skip space after the remark marker
+ }
+ // Check for pipes
+ else if ( ( c3 = Is_Pipe_Marker( c2, &pipe_mode ) ) )
+ {
+ itemline->kind = ITEM_LINE_PIPE;
+ itemline->pipe_mode = pipe_mode;
+ c = c3;
+ }
+ // Plain Items ...
+ else
+ {
+ itemline->kind = ITEM_LINE_PLAIN;
+ }
+ }
+ // Empty lines with remark markers and active tool
+ else if ( tool_active )
+ {
+ itemline->kind = ITEM_LINE_TOOL_BODY;
+ }
+ // Plain empty lines with remark markers...
+ else
+ {
+ itemline->kind = ITEM_LINE_PLAIN;
+ }
+ }
+ else
+ {
+ itemline->kind = ITEM_LINE_RAW;
+ /* The is raw code, so we do not want to have the
+ * whitespace stripped of
+ */
+ c = arg_header->lines[arg_item->begin_index + i];
+ c = ExpandTab( c );
+ }
+
+ if ( ( !Works_Like_SourceItem( arg_item->type ) &&
+ ( itemline->kind != ITEM_LINE_RAW ) ) ||
+ Works_Like_SourceItem( arg_item->type ) )
+ {
+ itemline->line = RB_StrDup( c );
+ itemline->format = 0;
+ arg_item->lines[j] = itemline;
+ ++j;
+ }
+ else
+ {
+ /* We dump the RAW item lines if we are not in a
+ * source item.
+ */
+ free( itemline );
+ }
+ }
+
+ if ( j > 0 )
+ {
+ /* And one empty line to mark the end of an item and
+ * to be able to store some additional formatting actions
+ */
+ itemline = malloc( sizeof( struct RB_Item_Line ) );
+ if ( !itemline )
+ {
+ RB_Panic( "Out of memory! %s (3)\n", "Copy_Lines_To_Item" );
+ }
+
+ itemline->kind = ITEM_LINE_END;
+ itemline->line = RB_StrDup( "" );
+ itemline->format = 0;
+ arg_item->lines[j] = itemline;
+
+ /* Store the real number of lines we copied */
+ assert( arg_item->no_lines >= ( j + 1 ) );
+ arg_item->no_lines = j + 1;
+ }
+ else
+ {
+ arg_item->no_lines = 0;
+ free( arg_item->lines );
+ arg_item->lines = NULL;
+ }
+ }
+ else
+ {
+ arg_item->no_lines = 0;
+ arg_item->lines = NULL;
+ }
+}
+
+
+/****f* Analyser/RB_Analyse_Items
+ * FUNCTION
+ * Locate the items in the header and create RB_Item structures for
+ * them.
+ * SYNPOPSIS
+ */
+static int Analyse_Items(
+ struct RB_header *arg_header )
+/*
+ * SOURCE
+ */
+{
+ int line_nr;
+ enum ItemType item_type = NO_ITEM;
+ struct RB_Item *new_item;
+ struct RB_Item *cur_item;
+
+ RB_Item_Lock_Reset( );
+
+ /* find the first item */
+ for ( line_nr = 0; line_nr < arg_header->no_lines; ++line_nr )
+ {
+ item_type = RB_Is_ItemName( arg_header->lines[line_nr] );
+ if ( item_type != NO_ITEM )
+ {
+ break;
+ }
+ }
+
+ /* and all the others */
+ while ( ( item_type != NO_ITEM ) && ( line_nr < arg_header->no_lines ) )
+ {
+ new_item = RB_Create_Item( item_type );
+ new_item->begin_index = line_nr;
+
+ /* Add the item to the end of the list of items. */
+ if ( arg_header->items )
+ {
+ for ( cur_item = arg_header->items; cur_item->next;
+ cur_item = cur_item->next )
+ {
+ /* Empty */
+ }
+ cur_item->next = new_item;
+ }
+ else
+ {
+ arg_header->items = new_item;
+ }
+ /* Find the next item */
+ for ( ++line_nr; line_nr < arg_header->no_lines; ++line_nr )
+ {
+ item_type = RB_Is_ItemName( arg_header->lines[line_nr] );
+ if ( item_type != NO_ITEM )
+ {
+ break;
+ }
+ }
+
+ /* This points to the last line in the item */
+ new_item->end_index = line_nr - 1;
+
+ assert( new_item->end_index >= new_item->begin_index );
+
+ /* Now analyse and copy the lines */
+ Copy_Lines_To_Item( arg_header, new_item );
+ Analyse_Item_Format( new_item );
+ /* Handy for debugging wiki formatting
+ * Dump_Item( new_item );
+ */
+ }
+
+ return 0;
+}
+
+/******/
+
+
+
+/****f* Analyser/ToBeAdded
+ * FUNCTION
+ * Test whether or not a header needs to be added to the
+ * list of headers. This implements the options
+ * --internal
+ * and
+ * --internalonly
+ * SYNPOPSIS
+ */
+static int ToBeAdded(
+ struct RB_Document *document,
+ struct RB_header *header )
+/*
+ * INPUTS
+ * o document -- a document (to determine the options)
+ * o header -- a header
+ * RESULT
+ * TRUE -- Add header
+ * FALSE -- Don't add header
+ * SOURCE
+ */
+{
+ int add = FALSE;
+
+ if ( header->is_internal )
+ {
+ if ( ( document->actions.do_include_internal ) ||
+ ( document->actions.do_internal_only ) )
+ {
+ add = TRUE;
+ }
+ else
+ {
+ add = FALSE;
+ }
+ }
+ else
+ {
+ if ( document->actions.do_internal_only )
+ {
+ add = FALSE;
+ }
+ else
+ {
+ add = TRUE;
+ }
+ }
+ return add;
+}
+
+/******/
+
+
+
+/****f* Analyser/Grab_Header
+ * FUNCTION
+ * Grab a header from a source file, that is scan a source file
+ * until the start of a header is found. Then search for the end
+ * of a header and store all the lines in between.
+ * SYNPOPSIS
+ */
+static struct RB_header *Grab_Header(
+ FILE *sourcehandle,
+ struct RB_Document *arg_document )
+/*
+ * INPUTS
+ * o sourcehandle -- an opened source file.
+ * OUTPUT
+ * o sourcehandle -- will point to the line following the end marker.
+ * RESULT
+ * 0 if no header was found, or a pointer to a new header otherwise.
+ * SOURCE
+ */
+{
+ struct RB_header *new_header = NULL;
+ int is_internal = 0;
+ struct RB_HeaderType *header_type = NULL;
+ int good_header = FALSE;
+ int reuse = FALSE;
+
+ do
+ {
+ good_header = FALSE;
+ header_type = RB_Find_Marker( sourcehandle, &is_internal, reuse );
+ reuse = FALSE;
+ if ( header_type )
+ {
+ struct RB_header *duplicate_header = NULL;
+ long previous_line = 0;
+
+ new_header = RB_Alloc_Header( );
+ new_header->htype = header_type;
+ new_header->is_internal = is_internal;
+
+ if ( Find_Header_Name( sourcehandle, new_header ) )
+ {
+ new_header->line_number = line_number;
+ RB_Say( "found header [line %5d]: \"%s\"\n", SAY_DEBUG,
+ line_number, new_header->name );
+ duplicate_header =
+ RB_Document_Check_For_Duplicate( arg_document,
+ new_header );
+ if ( duplicate_header )
+ {
+ /* Duplicate headers do not crash the program so
+ * we accept them. But we do warn the user.
+ */
+ RB_Warning
+ ( "A header with the name \"%s\" already exists.\n See %s(%d)\n",
+ new_header->name,
+ Get_Fullname( duplicate_header->owner->filename ),
+ duplicate_header->line_number );
+ }
+
+ if ( ( new_header->function_name =
+ Function_Name( new_header->name ) ) == NULL )
+ {
+ RB_Warning( "Can't determine the \"function\" name.\n" );
+ RB_Free_Header( new_header );
+ new_header = NULL;
+ }
+ else
+ {
+ if ( ( new_header->module_name =
+ Module_Name( new_header->name ) ) == NULL )
+ {
+ RB_Warning
+ ( "Can't determine the \"module\" name.\n" );
+ RB_Free_Header( new_header );
+ new_header = NULL;
+ }
+ else
+ {
+ previous_line = line_number;
+ if ( Find_End_Marker( sourcehandle, new_header ) ==
+ 0 )
+ {
+ RB_Warning
+ ( "found header on line %d with name \"%s\"\n"
+ " but I can't find the end marker\n",
+ previous_line, new_header->name );
+ /* Reuse the current line while finding the next
+ * Marking using RB_Find_Marker()
+ */
+ reuse = TRUE;
+ RB_Free_Header( new_header );
+ new_header = NULL;
+ }
+ else
+ {
+ RB_Say( "found end header [line %5d]:\n",
+ SAY_DEBUG, line_number );
+ /* Good header found, we can stop */
+ good_header = TRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ RB_Warning( "found header marker but no name\n" );
+ RB_Free_Header( new_header );
+ new_header = NULL;
+ }
+ }
+ else
+ {
+ /* end of the file */
+ good_header = TRUE;
+ }
+ }
+ while ( !good_header );
+ return new_header;
+}
+
+/*******/
+
+
+
+/****f* Analyser/Module_Name
+ * FUNCTION
+ * Get the module name from the header name. The header name will be
+ * something like
+ *
+ * module/functionname.
+ *
+ * SYNPOPSIS
+ */
+static char *Module_Name(
+ char *header_name )
+/*
+ * INPUTS
+ * o header_name -- a pointer to a nul terminated string.
+ * RESULT
+ * Pointer to the modulename. You're responsible for freeing it.
+ * SEE ALSO
+ * Function_Name()
+ * SOURCE
+ */
+{
+ char *cur_char;
+ char c;
+ char *name = NULL;
+
+ assert( header_name );
+
+ for ( cur_char = header_name; *cur_char && *cur_char != '/'; ++cur_char );
+ if ( *cur_char )
+ {
+ c = *cur_char;
+ *cur_char = '\0';
+ name = RB_StrDup( header_name );
+ *cur_char = c;
+ }
+ return name;
+}
+
+/******/
+
+
+
+/****f* Analyser/Function_Name
+ * FUNCTION
+ * A header name is consists of two parts. The module name and
+ * the function name. This returns a pointer to the function name.
+ * The name "function name" is a bit obsolete. It is really the name
+ * of any of objects that can be documented; classes, methods,
+ * variables, functions, projects, etc.
+ * SYNOPSIS
+ */
+static char *Function_Name(
+ char *header_name )
+/*
+ * SOURCE
+ */
+{
+ char *cur_char;
+ char *name;
+
+ name = NULL;
+ if ( ( cur_char = header_name ) != NULL )
+ {
+ for ( ; *cur_char != '\0'; ++cur_char )
+ {
+ if ( '/' == *cur_char )
+ {
+ ++cur_char;
+ if ( *cur_char )
+ {
+ name = cur_char;
+ break;
+ }
+ }
+ }
+ }
+ if ( name )
+ {
+ return RB_StrDup( name );
+ }
+ else
+ {
+ return ( name );
+ }
+}
+
+/*** Function_Name ***/
+
+
+/****f* Analyser/RB_Find_Marker
+ * NAME
+ * RB_Find_Marker -- Search for header marker in document.
+ * FUNCTION
+ * Read document file line by line, and search each line for
+ * any of the headers defined in the array header_markers (OR
+ * if using the -rh switch, robo_head)
+ * SYNOPSIS
+ */
+static struct RB_HeaderType *RB_Find_Marker(
+ FILE *document,
+ int *is_internal,
+ int reuse_previous_line )
+/*
+ * INPUTS
+ * document - pointer to the file to be searched.
+ * the gobal buffer line_buffer.
+ * OUTPUT
+ * o document will point to the line after the line with
+ * the header marker.
+ * o is_internal will be TRUE if the header is an internal
+ * header.
+ * RESULT
+ * o header type
+ * BUGS
+ * Bad use of feof(), fgets().
+ * SEE ALSO
+ * Find_End_Marker
+ * SOURCE
+ */
+{
+ int found;
+ char *cur_char;
+ struct RB_HeaderType *header_type = 0;
+
+ cur_char = NULL;
+ found = FALSE;
+ while ( !feof( document ) && !found )
+ {
+ if ( reuse_previous_line )
+ {
+ /* reuse line in the line_buffer */
+ reuse_previous_line = FALSE;
+ }
+ else
+ {
+ RB_FreeLineBuffer( );
+ myLine = RB_ReadWholeLine( document, line_buffer, &readChars );
+ }
+ if ( !feof( document ) )
+ {
+ line_number++;
+ found = RB_Is_Begin_Marker( myLine, &cur_char );
+ if ( found )
+ {
+ header_type = AnalyseHeaderType( &cur_char, is_internal );
+ RB_Say( "found header marker of type %s\n", SAY_DEBUG,
+ header_type->indexName );
+ }
+ }
+ }
+
+ return header_type;
+}
+
+/******** END RB_Find_Marker ******/
+
+
+/****f* Analyser/AnalyseHeaderType
+ * FUNCTION
+ * Determine the type of the header.
+ * SYNOPSIS
+ */
+struct RB_HeaderType *AnalyseHeaderType(
+ char **cur_char,
+ int *is_internal )
+/*
+ * INPUTS
+ * o cur_char -- pointer to the header type character
+ * OUTPUT
+ * o is_internal -- indicates if it is an internal header or not.*
+ * o cur_char -- points to the header type character
+ * RESULT
+ * o pointer to a RB_HeaderType
+ * SOURCE
+ */
+{
+ struct RB_HeaderType *headertype = 0;
+
+ *is_internal = RB_IsInternalHeader( **cur_char );
+
+ if ( *is_internal )
+ {
+ /* Skip the character */
+ ++( *cur_char );
+ }
+ headertype = RB_FindHeaderType( **cur_char );
+ if ( !headertype )
+ {
+ RB_Panic( "Undefined headertype (%c)\n", **cur_char );
+ }
+
+ return headertype;
+}
+
+/*******/
+
+
+
+/****f* Analyser/Find_End_Marker
+ * FUNCTION
+ * Scan and store all lines from a source file until
+ * an end marker is found.
+ * SYNOPSIS
+ */
+static int Find_End_Marker(
+ FILE *document,
+ struct RB_header *new_header )
+/*
+ * INPUTS
+ * o document -- a pointer to an opened source file.
+ * OUTPUT
+ * o new_header -- the lines of source code will be added
+ * here.
+ * RESULT
+ * o TRUE -- an end marker was found.
+ * o FALSE -- no end marker was found while scanning the
+ * source file.
+ * SOURCE
+ */
+{
+ int found = FALSE;
+ unsigned int no_lines = 0;
+ unsigned int max_no_lines = 10;
+ char **lines = NULL;
+ char **new_lines = NULL;
+ char *dummy;
+
+ lines = ( char ** ) calloc( max_no_lines, sizeof( char * ) );
+ if ( !lines )
+ {
+ RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" );
+ }
+
+ while ( !feof( document ) )
+ {
+ RB_FreeLineBuffer( );
+ myLine = RB_ReadWholeLine( document, line_buffer, &readChars );
+ ++line_number; /* global linecounter, koessi */
+ if ( RB_Is_Begin_Marker( myLine, &dummy ) )
+ {
+ /* Bad... found a begin marker but was expecting to
+ find an end marker. Panic... */
+ found = FALSE;
+ return found;
+ }
+ else if ( RB_Is_End_Marker( myLine ) )
+ {
+ RB_Say( "Found end marker \"%s\"", SAY_DEBUG, myLine );
+ found = TRUE;
+ break;
+ }
+ else
+ {
+ unsigned int n;
+ char *line;
+
+ line = RB_StrDup( myLine );
+ n = strlen( line );
+ assert( n > 0 );
+ assert( line[n - 1] == '\n' );
+ /* Strip CR */
+ line[n - 1] = '\0';
+ lines[no_lines] = line;
+ ++no_lines;
+ if ( no_lines == max_no_lines )
+ {
+ max_no_lines *= 2;
+ new_lines = realloc( lines, max_no_lines * sizeof( char * ) );
+
+ if ( !new_lines )
+ {
+ RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" );
+ }
+
+ lines = new_lines;
+ }
+ }
+ }
+
+ new_header->no_lines = no_lines;
+ new_header->lines = lines;
+
+ return found;
+}
+
+
+/******/
+
+
+/* TODO Documentation */
+static void Remove_Trailing_Asterics(
+ char *line )
+{
+ int i = strlen( line ) - 1;
+
+ for ( ; ( i > 0 ) && utf8_isspace( line[i] ); i-- )
+ {
+ /* Empty */
+ }
+ for ( ; ( i > 0 ) && ( line[i] == '*' ); i-- )
+ {
+ line[i] = ' ';
+ }
+}
+
+
+/****if* Utilities/RB_WordWithSpacesLen
+ * SYNOPSIS
+ */
+static int RB_WordWithSpacesLen(
+ char *str )
+/*
+ * FUNCTION
+ * get the amount of bytes until next separator character or ignore character
+ * or end of line
+ * INPUTS
+ * char *str -- the line
+ * RESULT
+ * int -- length of the next word or 0
+ * SEE ALSO
+ * RB_Find_Header_Name()
+ * SOURCE
+ */
+{
+ int len;
+ char c;
+
+ for ( len = 0; ( ( c = *str ) != '\0' ) && ( c != '\n' ); ++str, ++len )
+ {
+ // Look for header truncating characters
+ if ( Find_Parameter_Char( &( configuration.header_separate_chars ),
+ c ) != NULL
+ ||
+ Find_Parameter_Char( &( configuration.header_ignore_chars ),
+ c ) != NULL )
+ {
+ // and exit loop if any found
+ break;
+ }
+ }
+ /* Don't count any of the trailing spaces. */
+ if ( len )
+ {
+ --str;
+ for ( ; utf8_isspace( *str ); --len, --str )
+ {
+ /* empty */
+ }
+ }
+ return ( len );
+}
+
+/*** RB_WordWithSpacesLen ***/
+
+
+/* TODO Documentation */
+static int Find_Header_Name(
+ FILE *fh,
+ struct RB_header *hdr )
+{
+ char *cur_char = myLine;
+ char **names = NULL;
+ int num = 0;
+
+ Remove_Trailing_Asterics( cur_char );
+ skip_while( *cur_char != '*' );
+ skip_while( !utf8_isspace( *cur_char ) );
+ skip_while( utf8_isspace( *cur_char ) );
+ while ( *cur_char )
+ {
+ int length = RB_WordWithSpacesLen( cur_char );
+
+ if ( length == 0 )
+ break;
+ names = realloc( names, ( ++num ) * sizeof *names );
+
+ if ( !names )
+ {
+ RB_Panic( "Out of memory! %s()\n", "Find_Header_Name" );
+ }
+
+ names[num - 1] = RB_StrDupLen( cur_char, length );
+ /* printf("%c adding name = %s\n", num > 1 ? ' ' : '*', names[ num - 1 ] ); */
+ cur_char += length;
+ if ( Find_Parameter_Char( &( configuration.header_separate_chars ),
+ *cur_char ) )
+ {
+ for ( cur_char++; utf8_isspace( *cur_char ); cur_char++ );
+ /* End of line reach, but comma encountered, more headernames follow on next line */
+ if ( *cur_char == 0 )
+ {
+ /* Skip comment */
+ RB_FreeLineBuffer( );
+ myLine = RB_ReadWholeLine( fh, line_buffer, &readChars );
+ line_number++;
+ for ( cur_char = myLine; *cur_char && !utf8_isalpha( *cur_char );
+ cur_char++ );
+ }
+ }
+ }
+ hdr->names = names;
+ hdr->no_names = num;
+ hdr->name = num ? names[0] : NULL;
+ return num;
+}
+
+/***** Find_Header_Name *****/
--- /dev/null
+#ifndef ROBODOC_ANALYSER_H
+#define ROBODOC_ANALYSER_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "document.h"
+
+void RB_Analyse_Document(
+ struct RB_Document *arg_document );
+
+#endif /* ROBODOC_ANALYSER_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/* vi: spell ff=unix
+ */
+/****h* ROBODoc/ASCII_Generator
+ * NAME
+ * ASCII_Generator -- Generator for ASCII output
+ * FUNCTION
+ * Plain ascii output, no formatting.
+ * MODIFICATION HISTORY
+ * 2003-06-17 Frans Slothouber V1.0
+ *******
+ * $Id: ascii_generator.c,v 1.21 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "ascii_generator.h"
+#include "util.h"
+#include "robodoc.h"
+#include "globals.h"
+#include "items.h"
+#include "headers.h"
+#include "headertypes.h"
+#include "generator.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+/*x**h* ROBODoc/ASCII_Generator
+ * FUNCTION
+ * Generator for plain ASCII output
+ *******
+ */
+
+
+/*x**f* ASCII_Generator/RB_ASCII_Generate_Doc_Start
+ * NAME
+ * RB_ASCII_Generate_Doc_Start --
+ ******
+ */
+
+void RB_ASCII_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc )
+{
+ USE( src_name );
+ USE( name );
+ USE( toc );
+
+ if ( course_of_action.do_toc )
+ {
+ fprintf( dest_doc, "TABLE OF CONTENTS\n" );
+ /* TODO TOC */
+#if 0
+ for ( cur_header = first_header, header_nr = 1;
+ cur_header; cur_header = cur_header->next_header, header_nr++ )
+ {
+ if ( cur_header->name && cur_header->function_name )
+ {
+ fprintf( dest_doc, "%4.4d %s\n",
+ header_nr, cur_header->name );
+ }
+ }
+#endif
+ fputc( '\f', dest_doc );
+ }
+}
+
+/*x**f* ASCII_Generator/RB_ASCII_Generate_Doc_End
+ * NAME
+ * RB_ASCII_Generate_Doc_End --
+ ******
+ */
+
+void RB_ASCII_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ /* Empty */
+}
+
+/*x**f* ASCII_Generator/RB_ASCII_Generate_Header_Start
+ * NAME
+ * RB_ASCII_Generate_Header_Start --
+ ******
+ */
+
+void RB_ASCII_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ fprintf( dest_doc, "%s", cur_header->name );
+ fprintf( dest_doc, "\n\n" );
+}
+
+void RB_ASCII_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+ fprintf( dest_doc,
+ "\n---------------------------------------------------------------------------\n" );
+ /* form-feeds are annoying fputc( '\f', dest_doc ); */
+}
+
+/*x**f* ASCII_Generator/RB_ASCII_Generate_Empty_Item
+ * NAME
+ * RB_ASCII_Generate_Empty_Item --
+ ******
+ */
+
+void RB_ASCII_Generate_Empty_Item(
+ FILE *dest )
+{
+ USE( dest );
+ /* Empty */
+}
+
+/* TODO Documentation */
+void RB_ASCII_Generate_String(
+ FILE *dest,
+ char *string )
+{
+ fprintf( dest, "%s", string );
+}
+
+/* TODO Documentation */
+void RB_ASCII_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "%s", name );
+}
+
+/* TODO Documentation */
+char *RB_ASCII_Get_Default_Extension(
+ void )
+{
+ return ( ".txt" );
+}
+
+/* TODO Documentation */
+void RB_ASCII_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "%s\n", name );
+}
+
+
+/* TODO Documentation */
+void RB_ASCII_Generate_Item_Begin(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+
+/* TODO Documentation */
+void RB_ASCII_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ fputc( c, dest_doc );
+}
+
+
+/* TODO Documentation */
+void RB_ASCII_Generate_Item_End(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+
+static int section_counters[ASCII_MAX_SECTION_DEPTH];
+void RB_ASCII_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header )
+{
+ int i;
+
+ ++section_counters[depth];
+ for ( i = depth + 1; i < ASCII_MAX_SECTION_DEPTH; ++i )
+ {
+ section_counters[i] = 0;
+ }
+ if ( depth < ASCII_MAX_SECTION_DEPTH )
+ {
+ if ( !( course_of_action.do_sectionnameonly ) )
+ {
+ for ( i = 1; i <= depth; ++i )
+ {
+ fprintf( dest_doc, "%d.", section_counters[i] );
+ }
+ fprintf( dest_doc, " " );
+ }
+
+ // Print Header "first" name
+ RB_ASCII_Generate_String( dest_doc, name );
+
+ // Print further names
+ for ( i = 1; i < header->no_names; i++ )
+ {
+ fprintf( dest_doc, ( i % header_breaks ) ? ", " : ",\n" );
+ RB_ASCII_Generate_String( dest_doc, header->names[i] );
+ }
+
+ // Include module name if not sectionnameonly
+ if ( !( course_of_action.do_sectionnameonly ) )
+ {
+ fprintf( dest_doc, " [ " );
+ RB_ASCII_Generate_String( dest_doc, header->htype->indexName );
+ fprintf( dest_doc, " ]" );
+ }
+ }
+ else
+ {
+ /* too deep, don't do anything. */
+ assert( 0 );
+ }
+}
+
+void RB_ASCII_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ USE( depth );
+ /* Empty */
+}
--- /dev/null
+#ifndef ROBODOC_ASCII_GENERATOR_H
+#define ROBODOC_ASCII_GENERATOR_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+
+
+#define ASCII_MAX_SECTION_DEPTH 7
+
+void RB_ASCII_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc );
+void RB_ASCII_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+void RB_ASCII_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_ASCII_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_ASCII_Generate_Index(
+ FILE *dest,
+ char *source );
+void RB_ASCII_Generate_Empty_Item(
+ FILE *dest );
+void RB_ASCII_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+char *RB_ASCII_Get_Default_Extension(
+ void );
+void RB_ASCII_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+void RB_ASCII_Generate_Item_Begin(
+ FILE *dest_doc );
+
+void RB_ASCII_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_ASCII_Generate_Item_End(
+ FILE *dest_doc );
+
+void RB_ASCII_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header );
+
+void RB_ASCII_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+
+#endif /* ROBODOC_ASCII_GENERATOR_H */
--- /dev/null
+/* Source/config.h.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* default location for robodoc.rc */
+#undef ROBO_PREFIX
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
--- /dev/null
+#!/usr/bin/perl -w
+#****h* Module/css_to_c.pl
+# USAGE:
+# ./css_to_c.pl
+#
+# FUNCTION
+# Turns a .css file into a piece of C code.
+#
+# AUTHOR
+# Frans Slothouber (FSL), <rfsber@xs4all.nl> Gumpu Consulting
+# CREATED:
+# 6-7-2006 12:45:01 W. Europe Standard Time
+# REVISION: ---
+#
+#******************************************************************************
+# $Id: css_to_c.pl,v 1.4 2006/07/27 19:12:18 gumpu Exp $
+
+use strict;
+use warnings;
+use IO::File;
+
+
+# First preprocess the CSS code.
+my $robodoc_css_file_name = "robodoc_basic_style.css";
+my $robodoc_css_file = IO::File->new("<$robodoc_css_file_name") or die;
+my @css = <$robodoc_css_file>;
+$robodoc_css_file->close();
+
+# Remove \n
+# Replace all % by %%
+# Replace all " by \"
+# wrap inside a " .. \n"
+@css = map { chomp($_); s/%/%%/g; s/"/\\"/g; "\"$_\\n\"\n" } @css;
+
+# Split the whole file into several parts each with
+# it's own fprintf().
+# This to avoid compiler warning about string that
+# are too long.
+my @split_css_code = ();
+my $l = 0; # Length of the string
+push( @split_css_code, " fprintf( css_file,\n" );
+foreach my $line ( @css )
+{
+ push( @split_css_code, $line );
+ $l += length( $line );
+ if ( $l > 2000 ) {
+ $l = 0;
+ push( @split_css_code, " );\n" );
+ push( @split_css_code, " fprintf( css_file,\n" );
+ }
+}
+push( @split_css_code, " );\n" );
+
+
+# Insert CSS code into html_generator.c
+
+# Read the original code.
+my $html_generator_file = IO::File->new("<html_generator.c") or die;
+my @code = <$html_generator_file>;
+$html_generator_file->close();
+
+# Create a backup.
+my $html_generator_file_bak = IO::File->new(">html_generator_bak.c") or die;
+print $html_generator_file_bak @code;
+$html_generator_file_bak->close();
+
+# Merge the C code with the CSS code.
+my @new_code = ();
+my $skip = 0;
+foreach my $line ( @code ) {
+ if ( $line =~ m/BEGIN\sBEGIN\s/ ) {
+ $skip = 1;
+ push( @new_code, $line );
+ push( @new_code, @split_css_code );
+
+ } elsif ( $line =~ m/END\sEND\s/ ) {
+ push( @new_code, $line );
+ $skip = 0;
+ } else {
+ if ( $skip ) {
+ #nothing
+ } else {
+ push( @new_code, $line );
+ }
+ }
+}
+
+# Write the result.
+$html_generator_file = IO::File->new(">html_generator.c") or die;
+
+print $html_generator_file @new_code;
+
+$html_generator_file->close();
+
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Directory
+ * NAME
+ * This module contains function to scan a directory tree and to
+ * create a RB_Directory structure. Most of the OS dependend parts
+ * of ROBODoc can be found in this module..
+ *
+ * This module currently works under:
+ * o Cygwin http://cygwin.com
+ * o Redhad 7.3 Linux
+ * o VC++ under Windows NT
+ * o MINGW http://www.mingw.org/
+ * o OS/X
+ *
+ * AUTHOR
+ * Frans Slothouber
+ *****
+ * $Id: directory.c,v 1.41 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include "robodoc.h"
+#include "directory.h"
+#include "util.h"
+#include "globals.h"
+#include "roboconfig.h"
+
+#if defined (RB_MSVC) || defined(__MINGW32__)
+
+# include <io.h>
+# include <sys/types.h>
+
+#else
+
+# if defined _DIRENT_HAVE_D_TYPE
+ /* Empty */
+# else
+# include <sys/stat.h>
+# endif
+
+ /* no dirent in strict ansi !!! */
+# include <dirent.h>
+
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef DMALLOC
+# include <dmalloc.h>
+#endif
+
+
+/****v* Directory/content_buffer
+ * FUNCTION
+ * Temporary buffer file file content and filenames.
+ * SOURCE
+ */
+
+#define RB_CBUFFERSIZE 8191
+char content_buffer[RB_CBUFFERSIZE + 1];
+
+/*****/
+
+
+/* Local functions */
+static int RB_Is_PathCharacter(
+ int c );
+
+
+#if defined (RB_MSVC) || defined(__MINGW32__)
+/* empty */
+#else
+
+/****f* Directory/RB_FileType
+ * FUNCTION
+ * Determine the type of the file. This function is used for all
+ * compilers except VC++.
+ *
+ * If _DIRENT_HAVE_D_TYPE is defined we can find the filetype
+ * directly in the a_direntry. If not we have to stat the file to
+ * find out.
+ * SYNOPSIS
+ */
+T_RB_FileType RB_FileType(
+ char *arg_pathname,
+ struct dirent *a_direntry )
+/*
+ * INPUTS
+ * o arg_pathname -- the path leading up to this file
+ * o a_direntry -- a directory entry read by readdir()
+ * RESULT
+ * The type of the file.
+ ******
+ */
+{
+ T_RB_FileType file_type = RB_FT_UNKNOWN;
+
+#if defined _DIRENT_HAVE_D_TYPE
+ if ( a_direntry->d_type == DT_REG )
+ {
+ file_type = RB_FT_FILE;
+ }
+ else if ( a_direntry->d_type == DT_DIR )
+ {
+ file_type = RB_FT_DIRECTORY;
+ }
+ else
+ {
+ file_type = RB_FT_UNKNOWN;
+ }
+#endif
+ if ( file_type == RB_FT_UNKNOWN )
+ {
+ char *file_name = a_direntry->d_name;
+ struct stat filestat;
+ int result;
+
+ /* Either we do not have d_type, or it gives
+ * no information, so we try it a different
+ * way. (BUG 715778)
+ */
+ content_buffer[0] = '\0';
+ strcat( content_buffer, arg_pathname );
+ if ( content_buffer[strlen( content_buffer ) - 1] != '/' )
+ {
+ strcat( content_buffer, "/" );
+ }
+ strcat( content_buffer, file_name );
+ result = stat( content_buffer, &filestat );
+ if ( result == 0 )
+ {
+ if ( S_ISREG( filestat.st_mode ) )
+ {
+ file_type = RB_FT_FILE;
+ }
+ else if ( S_ISDIR( filestat.st_mode ) )
+ {
+ file_type = RB_FT_DIRECTORY;
+ }
+ else
+ {
+ file_type = RB_FT_UNKNOWN;
+ }
+ }
+ else
+ {
+ /* Some error occurred while statting the file */
+ }
+ }
+ return file_type;
+}
+#endif
+
+/****f* Directory/RB_Directory_Insert_RB_Path
+ * SYNOPSIS
+ */
+
+void RB_Directory_Insert_RB_Path(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Path *arg_rb_path )
+/*
+ * FUNCTION
+ * Insert a RB_Path into a RB_Directory.
+ * The RB_Path is added at the beginning of the list.
+ * SOURCE
+ */
+{
+ arg_rb_path->next = arg_rb_directory->first_path;
+ arg_rb_directory->first_path = arg_rb_path;
+}
+
+/*****/
+
+/****f* Directory/RB_Directory_Insert_RB_File
+ * SYNOPSIS
+ */
+void RB_Directory_Insert_RB_Filename(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Filename *arg_rb_filename )
+/*
+ * FUNCTION
+ * Insert an RB_File structure into a RB_Directory structure.
+ * The RB_File is added at the end of the list.
+ * SOURCE
+ */
+{
+ if ( arg_rb_directory->last == 0 )
+ {
+ arg_rb_directory->first = arg_rb_filename;
+ arg_rb_filename->next = 0;
+ arg_rb_directory->last = arg_rb_filename;
+ }
+ else
+ {
+ arg_rb_directory->last->next = arg_rb_filename;
+ arg_rb_filename->next = 0;
+ arg_rb_directory->last = arg_rb_filename;
+ }
+}
+
+/******/
+
+/****f* Directory/RB_Free_RB_Directory
+ * FUNCTION
+ * Free all the memory use by the RB_Directory structure.
+ * SYNOPSIS
+ */
+void RB_Free_RB_Directory(
+ struct RB_Directory *arg_directory )
+/*
+ * INPUTS
+ * o arg_directory -- the thing to be freed.
+ * SOURCE
+ */
+{
+ struct RB_Filename *rb_filename;
+ struct RB_Filename *rb_filename2;
+ struct RB_Path *rb_path;
+ struct RB_Path *rb_path2;
+
+ /* TODO Not complete... check for leaks. */
+ rb_filename = arg_directory->first;
+
+ while ( rb_filename )
+ {
+ rb_filename2 = rb_filename;
+ rb_filename = rb_filename->next;
+ RB_Free_RB_Filename( rb_filename2 );
+ }
+
+ rb_path = arg_directory->first_path;
+
+ while ( rb_path )
+ {
+ rb_path2 = rb_path;
+ rb_path = rb_path->next;
+ RB_Free_RB_Path( rb_path2 );
+ }
+
+ free( arg_directory );
+}
+
+/******/
+
+
+/****f* Directory/RB_Get_RB_Directory
+ * NAME
+ * RB_Get_RB_Directory -- get a RB_Directory structure
+ * FUNCTION
+ * Returns a RB_Directory structure to the give directory,
+ * specified by the path.
+ * This structure can then be uses to walk through all the
+ * files in the directory and it's subdirectories.
+ * SYNOPSIS
+ */
+struct RB_Directory *RB_Get_RB_Directory(
+ char *arg_rootpath_name,
+ char *arg_docroot_name )
+/*
+ * INPUTS
+ * arg_rootpath -- the name a the directory to get,
+ * a nul terminated string.
+ * arg_docroot_name -- the name of the directory the documentation
+ * file are stored in. This directory is
+ * skipped while scanning for sourcefiles.
+ * It can be NULL.
+ * RESULT
+ * A freshly allocated RB_Directory filled with source files.
+ * SOURCE
+ */
+{
+ struct RB_Directory *rb_directory;
+ struct RB_Path *doc_path = NULL;
+
+ rb_directory =
+ ( struct RB_Directory * ) malloc( sizeof( struct RB_Directory ) );
+ rb_directory->first = 0;
+ rb_directory->last = 0;
+ rb_directory->first_path = RB_Get_RB_Path( arg_rootpath_name );
+
+ if ( arg_docroot_name )
+ {
+ doc_path = RB_Get_RB_Path( arg_docroot_name );
+ }
+
+ RB_Fill_Directory( rb_directory, rb_directory->first_path, doc_path );
+ if ( ( RB_Number_Of_Filenames( rb_directory ) > 0 ) &&
+ ( RB_Number_Of_Paths( rb_directory ) > 0 ) )
+ {
+ RB_SortDirectory( rb_directory );
+ }
+ else
+ {
+
+ RB_Panic( "No files found! (Or all were filtered out)\n" );
+ }
+ return rb_directory;
+}
+
+/********/
+
+
+
+/****f* Directory/RB_Get_RB_SingleFileDirectory
+ * NAME
+ * RB_Get_RB_SingleFileDirectory -- get a RB_Directory structure
+ * SYNOPSIS
+ */
+struct RB_Directory *RB_Get_RB_SingleFileDirectory(
+ char *arg_fullpath )
+/*
+ * FUNCTION
+ * Returns a RB_Directory structure to the give directory,
+ * specified by the path that contains only a single file.
+ * This is used for the --singlefile option.
+ * INPUT
+ * o filename -- a filename. This may include the path.
+ * RESULT
+ * a freshly allocated RB_Directory that contains only
+ * a single file.
+ * SOURCE
+ */
+{
+ struct RB_Directory *rb_directory;
+ struct RB_Path *path;
+ char *pathname = NULL;
+ char *filename = NULL;
+
+ assert( arg_fullpath );
+
+ pathname = RB_Get_PathName( arg_fullpath );
+ filename = RB_Get_FileName( arg_fullpath );
+
+ if ( pathname )
+ {
+ path = RB_Get_RB_Path( pathname );
+ }
+ else
+ {
+ /* no directory was specified so we use
+ * the current directory
+ */
+ path = RB_Get_RB_Path( "./" );
+ }
+
+ rb_directory =
+ ( struct RB_Directory * ) malloc( sizeof( struct RB_Directory ) );
+ rb_directory->first = 0;
+ rb_directory->last = 0;
+
+ rb_directory->first_path = path;
+
+ RB_Directory_Insert_RB_Filename( rb_directory,
+ RB_Get_RB_Filename( filename, path ) );
+
+ return rb_directory;
+}
+
+/*******/
+
+
+/****f* Directory/RB_Fill_Directory
+ * NAME
+ * RB_Fill_Directory -- fill a RB_Directory structure
+ * SYNOPSIS
+ */
+void RB_Fill_Directory(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Path *arg_path,
+ struct RB_Path *arg_doc_path )
+/*
+ * FUNCTION
+ * Walks through all the files in the directory pointed to
+ * by arg_path and adds all the files to arg_rb_directory.
+ * This is a recursive function.
+ * INPUTS
+ * o arg_rb_directory -- the result.
+ * o arg_path -- the current path that is scanned.
+ * o arg_doc_path -- the path to the documentation files.
+ * RESULT
+ * a RB_Directory structure filled with all sourcefiles and
+ * subdirectories in arg_path.
+ * NOTE
+ * This a is a recursive function.
+ * SOURCE
+ */
+{
+
+#if defined (RB_MSVC) || defined(__MINGW32__)
+ struct _finddata_t c_file;
+ long hFile;
+ char *wildcard = NULL;
+ int len;
+
+ RB_Say( "Scanning %s\n", SAY_INFO, arg_path->name );
+ len = strlen( arg_path->name ) + strlen( "*.*" ) + 1;
+ wildcard = ( char * ) malloc( len );
+ assert( wildcard );
+ wildcard[0] = '\0';
+ strcat( wildcard, arg_path->name );
+ strcat( wildcard, "*.*" );
+ if ( ( hFile = _findfirst( wildcard, &c_file ) ) == -1L )
+ {
+ RB_Panic( "No files found!\n" );
+ }
+ else
+ {
+ int found = TRUE;
+
+ while ( found )
+ {
+ if ( ( c_file.attrib & _A_SUBDIR ) )
+ {
+ /* It's a directory */
+ if ( ( strcmp( ".", c_file.name ) == 0 )
+ || ( strcmp( "..", c_file.name ) == 0 ) )
+ {
+ /* Don't recurse into . or ..
+ because that will result in an infinite
+ loop. */
+ }
+ else
+ {
+ if ( RB_To_Be_Skipped( c_file.name ) )
+ {
+ /* User asked this directory to be skipped */
+ }
+ else
+ {
+ if ( course_of_action.do_nodesc )
+ {
+ /* Don't descent into the subdirectories */
+ }
+ else
+ {
+ struct RB_Path *rb_path =
+ RB_Get_RB_Path2( arg_path->name,
+ c_file.name );
+
+ if ( ( arg_doc_path
+ && strcmp( rb_path->name,
+ arg_doc_path->name ) )
+ || !arg_doc_path )
+ {
+ rb_path->parent = arg_path;
+ RB_Directory_Insert_RB_Path( arg_rb_directory,
+ rb_path );
+ RB_Fill_Directory( arg_rb_directory, rb_path,
+ arg_doc_path );
+ }
+ else
+ {
+ RB_Say( "skipping %s\n", SAY_INFO,
+ rb_path->name );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( RB_Is_Source_File( arg_path, c_file.name ) )
+ {
+ RB_Directory_Insert_RB_Filename( arg_rb_directory,
+ RB_Get_RB_Filename
+ ( c_file.name,
+ arg_path ) );
+ }
+ else
+ {
+ /* It's not a sourcefile so we skip it. */
+ }
+ }
+ /* Find the rest of the *.* files */
+ found = ( _findnext( hFile, &c_file ) == 0 );
+ }
+ _findclose( hFile );
+ }
+ free( wildcard );
+#else
+ struct dirent *a_direntry;
+ DIR *a_dirstream;
+
+ RB_Say( "Scanning %s\n", SAY_INFO, arg_path->name );
+ a_dirstream = opendir( arg_path->name );
+
+ if ( a_dirstream )
+ {
+ T_RB_FileType file_type;
+
+ for ( a_direntry = readdir( a_dirstream );
+ a_direntry; a_direntry = readdir( a_dirstream ) )
+ {
+ file_type = RB_FileType( arg_path->name, a_direntry );
+ if ( file_type == RB_FT_FILE )
+ {
+ /* It is a regular file. See if it is a sourcefile. */
+ if ( RB_Is_Source_File( arg_path, a_direntry->d_name ) )
+ {
+ /* It is, so we add it to the directory tree */
+ RB_Directory_Insert_RB_Filename( arg_rb_directory,
+ RB_Get_RB_Filename
+ ( a_direntry->d_name,
+ arg_path ) );
+ }
+ else
+ {
+ /* It's not a sourcefile so we skip it. */
+ }
+ }
+ else if ( file_type == RB_FT_DIRECTORY )
+ {
+ if ( ( strcmp( ".", a_direntry->d_name ) == 0 ) ||
+ ( strcmp( "..", a_direntry->d_name ) == 0 ) )
+ {
+ /* Don't recurse into . or ..
+ because that will result in an infinite */
+ }
+ else
+ {
+ if ( RB_To_Be_Skipped( a_direntry->d_name ) )
+ {
+ /* User asked this directory to be skipped */
+ }
+ else
+ {
+ if ( course_of_action.do_nodesc )
+ {
+ /* Don't descent into the subdirectories */
+ }
+ else
+ {
+ struct RB_Path *rb_path =
+ RB_Get_RB_Path2( arg_path->name,
+ a_direntry->d_name );
+
+ rb_path->parent = arg_path;
+ if ( ( arg_doc_path
+ && strcmp( rb_path->name,
+ arg_doc_path->name ) )
+ || !arg_doc_path )
+ {
+ RB_Directory_Insert_RB_Path( arg_rb_directory,
+ rb_path );
+ RB_Fill_Directory( arg_rb_directory, rb_path,
+ arg_doc_path );
+ }
+ else
+ {
+ RB_Say( "skipping %s\n", SAY_INFO,
+ rb_path->name );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Not a file and also not a directory */
+ }
+ }
+ }
+ closedir( a_dirstream );
+#endif
+}
+
+/*****/
+
+/****f* Directory/RB_Is_Source_File
+ * NAME
+ * RB_Is_Source_File -- Is a file a sourcefile?
+ * SYNOPSIS
+ */
+int RB_Is_Source_File(
+ struct RB_Path *path,
+ char *filename )
+/*
+ * FUNCTION
+ * This functions examines the content of a file to
+ * see whether or not it is a sourcefile.
+ *
+ * Currently it checks if there are no nul characters
+ * in the first 8191 characters of the file.
+ * SOURCE
+ */
+{
+ int is_source = 1;
+
+ if ( RB_Not_Accepted( filename ) )
+ {
+ /* File needs to be skipped based on it's name */
+ is_source = 0;
+ }
+ else
+ {
+ unsigned int size = 0;
+
+ /* Compute the length of the filename including
+ the path. */
+ size += strlen( path->name );
+ size += 1;
+ size += strlen( filename );
+ if ( size < RB_CBUFFERSIZE )
+ {
+ FILE *file;
+
+ /* We use the content_buffer buffer temporarily to
+ store the filename. */
+ content_buffer[0] = '\0';
+ strcat( content_buffer, path->name );
+ strcat( content_buffer, filename );
+ if ( ( file = fopen( content_buffer, "rb" ) ) )
+ {
+ int no_read;
+ no_read =
+ fread( content_buffer, sizeof( char ), RB_CBUFFERSIZE,
+ file );
+ if ( no_read > 10 )
+ {
+ char *c;
+
+ for ( c = content_buffer; no_read; --no_read, c++ )
+ {
+ if ( *c == 0 )
+ {
+ is_source = 0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* A file with only 9 characters can not
+ contain any source plus documentation. */
+ is_source = 0;
+ }
+ fclose( file );
+ }
+ else
+ {
+ /* The file could not be opened. */
+ is_source = 0;
+ }
+ }
+ else
+ {
+ /* The filename is longer than 8191 characters,
+ that's way too long... so we skip it. */
+ is_source = 0;
+ }
+ }
+ return is_source;
+}
+
+/*****/
+
+
+/****f* Directory/RB_To_Be_Skipped
+ * FUNCTION
+ * Test if a file should not be included in the list of source files
+ * that are scanned for documentation.
+ *
+ * This test is done based on the wildcard expressions specified
+ * in configuration.ignore_files.
+ * SYNOPSIS
+ */
+int RB_To_Be_Skipped(
+ char *filename )
+/*
+ * INPUTS
+ * o filename -- the name of the file
+ * SOURCE
+ */
+{
+ unsigned int i;
+ int skip = FALSE;
+
+ for ( i = 0; i < configuration.ignore_files.number; ++i )
+ {
+ if ( RB_Match( filename, configuration.ignore_files.names[i] ) )
+ {
+ skip = TRUE;
+ break;
+ }
+ }
+ return skip;
+}
+
+/******/
+
+
+/****f* Directory/RB_Not_Accepted
+ * FUNCTION
+ * Test if a file should be skipped,
+ * because it does not match a pattern in "accept files:"
+ *
+ * This test is done based on the wildcard expressions specified
+ * in configuration.accept_files.
+ * SYNOPSIS
+ */
+int RB_Not_Accepted(
+ char *filename )
+/*
+ * INPUTS
+ * o filename -- the name of the file
+ * SOURCE
+ */
+{
+ unsigned int i;
+ int skip = FALSE;
+
+ skip = RB_To_Be_Skipped( filename );
+
+ if ( !skip && configuration.accept_files.number > 0 )
+ {
+ skip = TRUE;
+ for ( i = 0; i < configuration.accept_files.number; ++i )
+ {
+ if ( RB_Match( filename, configuration.accept_files.names[i] ) )
+ {
+ RB_Say( "accept >%s< with >%s<\n", SAY_INFO, filename,
+ configuration.accept_files.names[i] );
+ skip = FALSE;
+ break;
+ }
+ }
+ }
+ return skip;
+}
+
+/******/
+
+
+/****f* Directory/RB_Get_FileName
+ * NAME
+ * RB_Get_PathName -- extract the file name
+ * SYNOPSIS
+ */
+char *RB_Get_FileName(
+ char *arg_fullpath )
+/*
+ * FUNCTION
+ * Given a full path to a file, that is a filename, or a filename
+ * prefixed with a pathname, return the filename.
+ * So
+ * "./filename" returns "filename"
+ * "/home/et/filename" returns "filename"
+ * "filename" return "filename"
+ * INPUTS
+ * arg_fullpath -- a full path to a file, with or without a path.
+ *
+ * RESULT
+ * 0 -- The full path did not contain a filename
+ * pointer to the filename -- otherwise.
+ * NOTES
+ * You are responsible for deallocating it.
+ * SOURCE
+ */
+{
+ int n;
+ int i;
+ int found;
+ char *result = 0;
+
+ assert( arg_fullpath );
+
+ n = strlen( arg_fullpath );
+
+ /* Try and find a path character ( ':' or '/' ) */
+ for ( found = FALSE, i = 0; i < n; ++i )
+ {
+ if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ /* The full path does not contain a pathname,
+ * so we can return the arg_fullpath as
+ * the filename.
+ */
+ result = RB_StrDup( arg_fullpath );
+ }
+ else
+ {
+ /* The full path does contain a pathname,
+ * strip this and return the remainder
+ */
+
+ for ( i = n - 1; i > 0; --i )
+ {
+ if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
+ {
+ assert( i < ( n - 1 ) );
+ result = RB_StrDup( &( arg_fullpath[i + 1] ) );
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/********/
+
+
+
+/****f* Directory/RB_Get_PathName
+ * NAME
+ * RB_Get_PathName -- extract the path name
+ * SYNOPSIS
+ */
+char *RB_Get_PathName(
+ char *arg_fullpath )
+/*
+ * FUNCTION
+ * Given a full path to a file, that is a filename, or a filename
+ * prefixed with a pathname, return the pathname.
+ * So
+ * "./filename" returns "./"
+ * "/home/et/filename" returns "/home/et/"
+ * "filename" return ""
+ * INPUTS
+ * arg_fullpath -- a full path to a file, with or without a path.
+ *
+ * RESULT
+ * 0 -- The full path did not contain a path
+ * pointer to the pathname -- otherwise.
+ * NOTES
+ * You are responsible for deallocating it.
+ * SOURCE
+ */
+{
+ int n;
+ int i;
+ int found;
+ char *result = 0;
+
+ assert( arg_fullpath );
+
+ n = strlen( arg_fullpath );
+
+ /* Try and find a path character ( ':' or '/' ) */
+ for ( found = FALSE, i = 0; i < n; ++i )
+ {
+ if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if ( found )
+ {
+ /* Copy the whole file name and then
+ replace the character after the
+ first path character found
+ counting from the back with a '\0'
+ */
+ result = RB_StrDup( arg_fullpath );
+
+ for ( i = n - 1; i > 0; --i )
+ {
+ if ( RB_Is_PathCharacter( result[i] ) )
+ {
+ assert( i < ( n - 1 ) );
+ result[i + 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*******/
+
+
+/****f* Directory/RB_Is_PathCharacter
+ * FUNCTION
+ * Test if a character is part of the group of
+ * characters that you would normally find in
+ * a path.
+ * SYNOPSIS
+ */
+static int RB_Is_PathCharacter(
+ int c )
+/*
+ * INPUTS
+ * c -- the character to be tested.
+ * RESULT
+ * TRUE -- it is a path character.
+ * FALSE -- it is not.
+ * SOURCE
+ */
+{
+ return ( ( c == ':' ) || ( c == '/' ) );
+}
+
+/******/
+
+
+/* Sort the files and paths */
+
+/* TODO FS Document */
+unsigned int RB_Number_Of_Filenames(
+ struct RB_Directory *arg_rb_directory )
+{
+ unsigned int number_of_filenames = 0;
+ struct RB_Filename *rb_filename = NULL;
+
+ for ( rb_filename = arg_rb_directory->first;
+ rb_filename;
+ ++number_of_filenames, rb_filename = rb_filename->next )
+ {
+ /* Empty */
+ }
+ return number_of_filenames;
+}
+
+
+/* TODO FS Document */
+unsigned int RB_Number_Of_Paths(
+ struct RB_Directory *arg_rb_directory )
+{
+ unsigned int number_of_paths = 0;
+ struct RB_Path *rb_path = NULL;
+
+ for ( rb_path = arg_rb_directory->first_path;
+ rb_path; ++number_of_paths, rb_path = rb_path->next )
+ {
+ /* Empty */
+ }
+ return number_of_paths;
+}
+
+/* TODO FS Document */
+int RB_Path_Compare(
+ void *p1,
+ void *p2 )
+{
+ struct RB_Path *path_1 = p1;
+ struct RB_Path *path_2 = p2;
+
+ return RB_Str_Case_Cmp( path_1->name, path_2->name );
+}
+
+/* TODO FS Document */
+int RB_Filename_Compare(
+ void *p1,
+ void *p2 )
+{
+ struct RB_Filename *filename_1 = p1;
+ struct RB_Filename *filename_2 = p2;
+
+ return RB_Str_Case_Cmp( filename_1->name, filename_2->name );
+}
+
+
+/* TODO FS Document */
+void RB_SortDirectory(
+ struct RB_Directory *arg_rb_directory )
+{
+ unsigned int number_of_filenames =
+ RB_Number_Of_Filenames( arg_rb_directory );
+ unsigned int number_of_paths =
+ RB_Number_Of_Paths( arg_rb_directory );
+ unsigned int i;
+ struct RB_Path *rb_path = NULL;
+ struct RB_Filename *rb_filename = NULL;
+ struct RB_Path **paths = NULL;
+ struct RB_Filename **filenames = NULL;
+
+ assert( number_of_filenames > 0 );
+ assert( number_of_paths > 0 );
+ paths = calloc( number_of_paths, sizeof( struct RB_Path * ) );
+ filenames = calloc( number_of_filenames, sizeof( struct RB_Filename * ) );
+
+
+ RB_Say( "Sorting Directory\n", SAY_INFO );
+ for ( i = 0, rb_path = arg_rb_directory->first_path;
+ rb_path; rb_path = rb_path->next )
+ {
+ assert( i < number_of_paths );
+ paths[i] = rb_path;
+ ++i;
+ }
+
+ for ( i = 0, rb_filename = arg_rb_directory->first;
+ rb_filename; rb_filename = rb_filename->next )
+ {
+ assert( i < number_of_filenames );
+ filenames[i] = rb_filename;
+ ++i;
+ }
+
+ RB_QuickSort( ( void ** ) paths, 0, number_of_paths - 1,
+ RB_Path_Compare );
+ RB_QuickSort( ( void ** ) filenames, 0, number_of_filenames - 1,
+ RB_Filename_Compare );
+
+ for ( i = 0; i < number_of_paths - 1; ++i )
+ {
+ paths[i]->next = paths[i + 1];
+ }
+ paths[number_of_paths - 1]->next = NULL;
+ arg_rb_directory->first_path = paths[0];
+
+ for ( i = 0; i < number_of_filenames - 1; ++i )
+ {
+ filenames[i]->next = filenames[i + 1];
+ }
+ filenames[number_of_filenames - 1]->next = NULL;
+ arg_rb_directory->first = filenames[0];
+ arg_rb_directory->last = filenames[number_of_filenames - 1];
+
+ free( paths );
+ free( filenames );
+}
--- /dev/null
+#ifndef ROBODOC_DIRECTORY_H
+#define ROBODOC_DIRECTORY_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "file.h"
+
+/****s* Directory/RB_Directory
+ * NAME
+ * RB_Directory -- the directory tree with the source files.
+ * FUNCTION
+ * Stores information about files in a directory tree.
+ * The whole structure consist of two linked lists. One for
+ * directory paths, and one for filenames.
+ * EXAMPLE
+ * The following show an example structure.
+ * RB_Directory RB_Path
+ * +-------+ +------+ +-------+ +-----------+
+ * | +-->| . |--->| ./sub |-->| ./sub/sub |
+ * | | +------+ +-------+ +-----------+
+ * | | ^ ^ ^
+ * | | | | |
+ * | | |-----------+ +------+ +------+
+ * | | | | | |
+ * | | | | | |
+ * | | +------+ +------+ +------+ +-------+
+ * | +-->| a.c |--->| b.c |-->| sa.c |-->| ssb.c |
+ * +-------+ +------+ +------+ +------+ +-------+
+ * RB_Filename
+ *
+ * ATTRIBUTES
+ * * first -- first RB_Filename in the list of files
+ * * current -- the last file that was returned in
+ * RB_Get_Next_Filename.
+ * * last -- the last RB_Filename in the list of files
+ * used for the insert operation
+ * * first_path -- first RB_Path in the list of paths.
+ * SOURCE
+ */
+
+struct RB_Directory
+{
+ struct RB_Filename *first; /* TODO should be called files */
+ struct RB_Filename *last;
+ struct RB_Path *first_path; /* TODO should be called paths */
+};
+
+/******/
+
+
+/****t* Directory/T_RB_FileType
+ * FUNCTION
+ * Constants for the two different filetypes that
+ * ROBODoc recognizes.
+ * SOURCE
+ */
+
+typedef enum
+{
+ RB_FT_DIRECTORY = 1,
+ RB_FT_FILE = 2,
+ RB_FT_UNKNOWN = 3
+} T_RB_FileType;
+
+/******/
+
+
+struct RB_Directory *RB_Get_RB_Directory(
+ char *arg_rootpath,
+ char *arg_docroot_name );
+struct RB_Directory *RB_Get_RB_SingleFileDirectory(
+ char *arg_fullpath );
+void RB_Dump_RB_Directory(
+ struct RB_Directory *arg_rb_directory );
+void RB_Free_RB_Directory(
+ struct RB_Directory *arg_directory );
+void RB_Directory_Insert_RB_Path(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Path *arg_rb_path );
+void RB_Directory_Insert_RB_Filename(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Filename *arg_rb_filename );
+
+void RB_Fill_Directory(
+ struct RB_Directory *arg_rb_directory,
+ struct RB_Path *arg_path,
+ struct RB_Path *arg_doc_path );
+int RB_Is_Source_File(
+ struct RB_Path *path,
+ char *filename );
+int RB_To_Be_Skipped(
+ char *filename );
+int RB_Not_Accepted(
+ char *filename );
+
+
+char *RB_Get_FileName(
+ char *arg_fullpath );
+char *RB_Get_PathName(
+ char *arg_fullpath );
+void RB_SortDirectory(
+ struct RB_Directory *arg_rb_directory );
+
+int RB_Path_Compare(
+ void *p1,
+ void *p2 );
+int RB_Filename_Compare(
+ void *p1,
+ void *p2 );
+unsigned int RB_Number_Of_Filenames(
+ struct RB_Directory *arg_rb_directory );
+unsigned int RB_Number_Of_Paths(
+ struct RB_Directory *arg_rb_directory );
+
+#endif /* ROBODOC_DIRECTORY_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Document
+ * FUNCTION
+ * This module contains functions to manipulate the central data
+ * structure (RB_Document) that contains information about the
+ * source files, and documentation files, and headers.
+ *
+ * The name is a bit confusing because it sort of implies that
+ * it contains the documentation extracted from the sourcefiles.
+ *
+ * For each run a RB_Document structure is created, it is filled
+ * by the analyser and directory module and then used by the
+ * generator module to create the documentation.
+ * MODIFICATION HISTORY
+ * * ????-??-?? Frans Slothouber V1.0
+ * * 2003-02-03 Frans Slothouber Refactoring
+ * * 2003-10-30 David White Removed unistd.h for Borland
+ *******
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined (RB_BCC) || defined( RB_MSVC )
+#else
+#include <unistd.h> /* Not used for Borland */
+#endif
+
+#include "robodoc.h"
+#include "document.h"
+#include "part.h"
+#include "path.h"
+#include "directory.h"
+#include "headers.h"
+#include "links.h"
+#include "util.h"
+#include <string.h>
+#include "generator.h"
+#include "file.h"
+#include "globals.h"
+
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+/****f* Document/RB_Document_Add_Part
+ * FUNCTION
+ * Add a new part to the document.
+ * SYNOPSIS
+ */
+void RB_Document_Add_Part(
+ struct RB_Document *document,
+ struct RB_Part *part )
+/*
+ * INPUTS
+ * o document -- the document the part is to be added to.
+ * o part -- the part to be added
+ * SOURCE
+ */
+{
+ part->next = document->parts;
+ document->parts = part;
+}
+
+/*****/
+
+
+/* TODO Documentation */
+void RB_Free_RB_Document_Parts(
+ struct RB_Document *document )
+{
+ if ( document->parts )
+ {
+ struct RB_Part *a_part = NULL;
+ struct RB_Part *a_part2 = NULL;
+
+ for ( a_part = document->parts; a_part; a_part = a_part2 )
+ {
+ a_part2 = a_part->next;
+ RB_Free_RB_Part( a_part );
+ }
+ }
+ document->parts = NULL;
+}
+
+
+/* TODO Documentation */
+void RB_Free_RB_Document(
+ struct RB_Document *document )
+{
+ RB_Free_RB_Document_Parts( document );
+ if ( document->headers )
+ {
+ unsigned long i;
+
+ for ( i = 0; i < document->no_headers; ++i )
+ {
+ RB_Free_Header( document->headers[i] );
+
+ }
+ free( document->headers );
+ }
+ free( document );
+}
+
+/****f* Document/RB_Document_Create_Parts
+ * FUNCTION
+ * Create all the parts of a document based on the sourcefiles in
+ * the source tree. This creates a new RB_Part for each file in
+ * the source tree.
+ * INPUTS
+ * o document -- the document for which the parts are generated.
+ * SOURCE
+ */
+
+void RB_Document_Create_Parts(
+ struct RB_Document *document )
+{
+ struct RB_Filename *i_file = NULL;
+
+ assert( document );
+ assert( document->srctree );
+
+ for ( i_file = document->srctree->first; i_file; i_file = i_file->next )
+ {
+ struct RB_Part *rbpart;
+
+ rbpart = RB_Get_RB_Part( );
+ RB_Part_Add_Source( rbpart, i_file );
+ RB_Document_Add_Part( document, rbpart );
+ }
+}
+
+/*******/
+
+
+/****f* Document/RB_Fill_Header_Filename
+ * FUNCTION
+ * Fill the file_name attribute of all headers based either on the
+ * part or the singledoc name. The file_name tells in which file
+ * the documentation for the header is to be stored.
+ * SYNOPSIS
+ */
+void RB_Fill_Header_Filename(
+ struct RB_Document *document )
+/*
+ * SOURCE
+ */
+{
+ struct RB_Part *i_part;
+
+ RB_Say( "Computing file_name attribute for all headers.\n", SAY_DEBUG );
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ for ( i_header = i_part->headers;
+ i_header; i_header = i_header->next )
+ {
+ if ( document->actions.do_singledoc )
+ {
+ i_header->file_name = document->singledoc_name;
+ }
+ else if ( document->actions.do_multidoc )
+ {
+ i_header->file_name = RB_Get_FullDocname( i_part->filename );
+ }
+ else if ( document->actions.do_singlefile )
+ {
+ i_header->file_name = document->singledoc_name;
+ }
+ else
+ {
+ assert( 0 );
+ }
+ }
+ }
+}
+
+/******/
+
+
+/****f* Document/RB_Document_Determine_DocFilePaths
+ * FUNCTION
+ * Determine the path of each of the documentation files based on
+ * the path of the source file and the documentation root path and
+ * the source root path.
+ * SYNOPSIS
+ */
+void RB_Document_Determine_DocFilePaths(
+ struct RB_Document *document )
+/*
+ * EXAMPLE
+ * srcpath = ./test/mysrc/sub1/sub2
+ * srcroot = ./test/mysrc/
+ * docroot = ./test/mydoc/
+ * ==>
+ * docpath = ./test/mydoc/sub1/sub2
+ * SOURCE
+ */
+{
+ struct RB_Path *path;
+ int docroot_length;
+ int srcroot_length;
+ int length;
+
+ assert( document->srctree );
+ assert( document->srcroot );
+ assert( document->docroot );
+
+ docroot_length = strlen( document->docroot->name );
+ srcroot_length = strlen( document->srcroot->name );
+
+ for ( path = document->srctree->first_path; path; path = path->next )
+ {
+ char *name;
+ char *new_name;
+ char *tail;
+
+ name = path->name;
+ length = strlen( name );
+ assert( length >= srcroot_length );
+ tail = name + srcroot_length;
+ new_name = calloc( docroot_length +
+ ( length - srcroot_length ) + 1, sizeof( char ) );
+ assert( new_name );
+ strcat( new_name, document->docroot->name );
+ if ( document->actions.do_no_subdirectories )
+ {
+ /* No subdirectory */
+ }
+ else
+ {
+ strcat( new_name, tail );
+ }
+ path->docname = new_name;
+ }
+}
+
+/******/
+
+
+/****f* Document/RB_Document_Create_DocFilePaths
+ * FUNCTION
+ * This function creates the whole document directory
+ * tree. It tests if the directories exist and if they
+ * do not the directory is created.
+ * SYNOPSIS
+ */
+void RB_Document_Create_DocFilePaths(
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * o document -- the document for which the tree is created.
+ * SOURCE
+ */
+{
+ struct RB_Path *path;
+
+ for ( path = document->srctree->first_path; path; path = path->next )
+ {
+ char *pathname = NULL;
+ char *c2 = NULL;
+
+ RB_Say( "Trying to create directory %s\n", SAY_INFO, path->docname );
+ /* Don't want to modify the docname in the path
+ structure. So we make a copy that we later
+ destroy. */
+
+ pathname = RB_StrDup( path->docname );
+ for ( c2 = pathname + 1; /* We skip the leading '/' */
+ *c2; ++c2 )
+ {
+ if ( *c2 == '/' )
+ {
+ struct stat dirstat;
+
+ *c2 = '\0'; /* Replace the '/' with a '\0'. */
+ /* We now have one of the paths leading up to the
+ total path. Test if it exists. */
+ if ( stat( pathname, &dirstat ) == 0 )
+ {
+ /* Path exists. */
+ }
+ else if ( ( strlen( pathname ) == 2 ) &&
+ ( utf8_isalpha( pathname[0] ) ) &&
+ ( pathname[1] == ':' ) )
+ {
+ /* Is is a drive indicator, ( A: B: C: etc )
+ * stat fails on this, but we should not
+ * create either, so we do nothing.
+ */
+ }
+ else
+ {
+ int result;
+
+#if defined(__MINGW32__) || defined( RB_MSVC )
+ result = mkdir( pathname );
+#else
+ result = mkdir( pathname, 0770 );
+#endif
+ if ( result == 0 )
+ {
+ /* Path was created. */
+ }
+ else
+ {
+ perror( NULL );
+ RB_Panic( "Can't create directory %s\n", pathname );
+ }
+ }
+ /* Put the '/' back in it's place. */
+ *c2 = '/';
+ }
+ }
+ free( pathname );
+ }
+}
+
+/*******/
+
+
+/* TODO Documentation */
+
+/*x**f*
+ * FUNCTION
+ * Compare two header types for sorting.
+ * RESULT
+ * -1 h1 < h2
+ * 0 h1 == h2
+ * 1 h1 > h2
+ * SOURCE
+ */
+
+int RB_CompareHeaders(
+ void *h1,
+ void *h2 )
+{
+ struct RB_header *header_1 = h1;
+ struct RB_header *header_2 = h2;
+
+ // Check for priorities
+ if ( header_1->htype->priority > header_2->htype->priority )
+ {
+ // Header 1 has higher priority
+ return -1;
+ }
+ else if ( header_1->htype->priority < header_2->htype->priority )
+ {
+ // Header 2 has higher priority
+ return 1;
+ }
+ else
+ {
+ // Priorities are equal
+ // Check if we sort on full name or just the function name
+ if ( course_of_action.do_sectionnameonly )
+ {
+ // Do not include parent name in sorting if they are not displayed
+ return RB_Str_Case_Cmp( header_1->function_name,
+ header_2->function_name );
+ }
+ else
+ {
+ // Sort on full name ( module/function )
+ return RB_Str_Case_Cmp( header_1->name, header_2->name );
+ }
+ }
+}
+
+/*****/
+
+/* TODO Documentation */
+void RB_Document_Sort_Headers(
+ struct RB_Document *document )
+{
+ struct RB_Part *i_part;
+ unsigned long part_count = 0;
+
+ RB_Say( "Sorting headers per part (file)\n", SAY_INFO );
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ /* Count the number of headers */
+ for ( part_count = 0, i_header = i_part->headers;
+ i_header; i_header = i_header->next )
+ {
+ part_count++;
+ }
+
+ if ( part_count )
+ {
+ /* Sort them */
+ struct RB_header **temp_headers =
+ calloc( part_count, sizeof( struct RB_header * ) );
+ unsigned int i = 0;
+
+ i_header = i_part->headers;
+ for ( i = 0; i < part_count; ++i )
+ {
+ assert( i_header );
+ temp_headers[i] = i_header;
+ i_header = i_header->next;
+ }
+ RB_QuickSort( ( void ** ) temp_headers, 0, part_count - 1,
+ RB_CompareHeaders );
+ i_part->headers = temp_headers[0];
+ i_part->headers->next = NULL;
+ i_header = temp_headers[0];
+ for ( i = 1; i < part_count; ++i )
+ {
+ assert( i_header );
+ i_header->next = temp_headers[i];
+ i_header = i_header->next;
+ }
+ temp_headers[part_count - 1]->next = NULL;
+ free( temp_headers );
+ }
+ }
+ RB_Say( "Sorting all headers\n", SAY_INFO );
+ RB_QuickSort( ( void ** ) document->headers, 0, document->no_headers - 1,
+ RB_CompareHeaders );
+}
+
+
+/****f* Document/RB_Document_Collect_Headers
+ * FUNCTION
+ * Create a table of pointers to all headers. This is done to
+ * have easy access to all heades without having to scan all
+ * RB_Parts.
+ * INPUTS
+ * o document -- the document for which the table is created.
+ * OUTPUT
+ * o document->headers
+ * o document->no_headers
+ * SOURCE
+ */
+
+void RB_Document_Collect_Headers(
+ struct RB_Document *document )
+{
+ struct RB_Part *i_part;
+ struct RB_header **headers; /* Pointer to an array of pointers RB_headers. */
+ unsigned long count = 0;
+ unsigned long part_count = 0;
+ unsigned long i = 0;
+
+ RB_Say( "Collecting all headers in a single table\n", SAY_INFO );
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ /* Count the number of headers */
+ for ( part_count = 0, i_header = i_part->headers;
+ i_header; i_header = i_header->next )
+ {
+ part_count++;
+ }
+ /* Compute the total count */
+ count += part_count;
+ }
+ headers =
+ ( struct RB_header ** ) calloc( count, sizeof( struct RB_header * ) );
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ for ( i_header = i_part->headers;
+ i_header; i_header = i_header->next )
+ {
+ headers[i] = i_header;
+ i++;
+ }
+ }
+ document->headers = headers;
+ document->no_headers = count;
+}
+
+/*******/
+
+/* TODO Documentation */
+
+struct RB_header *RB_Document_Check_For_Duplicate(
+ struct RB_Document *arg_document,
+ struct RB_header *hdr )
+{
+ struct RB_Part *i_part;
+
+ for ( i_part = arg_document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ for ( i_header = i_part->headers; i_header;
+ i_header = i_header->next )
+ {
+ int i;
+
+ if ( hdr == i_header )
+ continue;
+
+ for ( i = 0; i < hdr->no_names; i++ )
+ if ( strcmp( hdr->names[i], i_header->name ) == 0 )
+ return i_header;
+ }
+ }
+ return NULL;
+}
+
+
+/* TODO Documentation
+ If A is called qqqq/ffff and B is called ffff/zzzz then A is the
+ parent of B
+*/
+
+void RB_Document_Link_Headers(
+ struct RB_Document *document )
+{
+ unsigned long i;
+ unsigned long j;
+ struct RB_header *parent;
+ struct RB_header *child;
+ char *parent_name;
+ char *child_name;
+
+ RB_Say( "Linking all %d headers\n", SAY_INFO, document->no_headers );
+ for ( i = 0; i < document->no_headers; i++ )
+ {
+ parent = ( document->headers )[i];
+ parent_name = parent->function_name;
+ for ( j = 0; j < document->no_headers; j++ )
+ {
+ if ( i != j )
+ {
+ child = ( document->headers )[j];
+ child_name = child->module_name;
+ if ( strcmp( child_name, parent_name ) == 0 )
+ {
+ child->parent = parent;
+ }
+ }
+ }
+ }
+}
+
+
+void RB_Document_Split_Parts(
+ struct RB_Document *document )
+{
+ struct RB_Part *i_part = NULL;
+ struct RB_Part **new_parts = NULL;
+ int new_number_of_parts = 0;
+ int n = 0;
+ int i;
+
+ /* split eacht part in several parts. One for each header
+ * in the original part.
+ */
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+
+ for ( i_header = i_part->headers;
+ i_header; i_header = i_header->next )
+ {
+ ++new_number_of_parts;
+ }
+ }
+
+ new_parts = calloc( new_number_of_parts, sizeof( struct RB_Part * ) );
+
+ if ( new_parts )
+ {
+ /* Create new parts */
+
+ RB_Say( "Splitting parts based on headers.\n", SAY_DEBUG );
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ struct RB_header *i_header;
+ struct RB_header *i_next_header;
+
+ for ( i_header = i_part->headers;
+ i_header; i_header = i_next_header )
+ {
+ struct RB_Part *new_part;
+
+ i_next_header = i_header->next;
+
+ RB_Say( "Creating new part.\n", SAY_DEBUG );
+ new_part = RB_Get_RB_Part( );
+ RB_Part_Add_Source( new_part,
+ RB_Copy_RB_Filename( RB_Part_Get_Source
+ ( i_part ) ) );
+ /* remove header from i_part and add to new_part.
+ */
+ RB_Part_Add_Header( new_part, i_header );
+ assert( n < new_number_of_parts );
+ new_parts[n] = new_part;
+ ++n;
+ }
+ i_part->headers = NULL;
+ i_part->last_header = NULL;
+ }
+ /* Remove old part from document */
+ RB_Free_RB_Document_Parts( document );
+ /* Add new parts to document */
+ for ( i = 0; i < n; ++i )
+ {
+ RB_Document_Add_Part( document, new_parts[i] );
+ }
+ /* clean-up temp array */
+ free( new_parts );
+ }
+ else
+ {
+ RB_Panic( "Out of memory! RB_Document_Split_Parts()" );
+ }
+}
+
+/* TODO Documentation */
+void RB_Document_Determine_DocFileNames(
+ struct RB_Document *document )
+{
+ struct RB_Filename *filename;
+ unsigned int length = 0;
+ char *name;
+ char *c;
+
+ struct RB_Part *part;
+
+ assert( document->actions.do_multidoc );
+
+ for ( part = document->parts; part; part = part->next )
+ {
+
+ filename = part->filename;
+ /* turn mysource.c into mysource_c.html
+ First find the total length. */
+ length = strlen( filename->name );
+ /* add one for the '.' */
+ ++length;
+
+ if ( document->actions.do_one_file_per_header )
+ {
+ struct RB_header *i_header = part->headers;
+
+ assert( i_header );
+ /* add the name of the header to the filename */
+ /* We make this twice as long because some of the
+ * characters in the file are escaped to 2 hexadecimal
+ * digits.
+ */
+ length += 2 * strlen( i_header->name );
+ }
+
+ length += RB_Get_Len_Extension( document->extension );
+ /* plus one for the '\0' */
+ ++length;
+ name = ( char * ) calloc( length, sizeof( char ) );
+ assert( name );
+ strcat( name, filename->name );
+ for ( c = name; *c != '\0'; c++ )
+ {
+ if ( *c == '.' )
+ {
+ *c = '_';
+ }
+ }
+
+ if ( document->actions.do_one_file_per_header )
+ {
+ unsigned int i;
+ struct RB_header *i_header = part->headers;
+
+ assert( i_header );
+ /* add the name of the header to the filename */
+ for ( i = 0; i < strlen( i_header->name ); ++i )
+ {
+ if ( utf8_isalnum( i_header->name[i] ) )
+ {
+ sprintf( c, "%c", i_header->name[i] );
+ c++;
+ }
+ else
+ {
+ sprintf( c, "%2X", i_header->name[i] );
+ c++;
+ c++;
+ }
+ }
+ }
+ RB_Add_Extension( document->extension, name );
+
+ RB_Say( "Filename for part is %s\n", SAY_DEBUG, name );
+ part->filename->docname = name;
+ }
+}
+
+/****f* Document/RB_Open_SingleDocumentation
+ * FUNCTION
+ * Open the file that will contain the documentation in
+ * case we create a single document.
+ * SYNOPSIS
+ */
+FILE *RB_Open_SingleDocumentation(
+ struct RB_Document *document )
+/*
+ * RESULT
+ * An opened file.
+ * SOURCE
+ */
+{
+ FILE *file;
+ static char *default_name = "singledoc";
+ char *name = NULL;
+ size_t size = 0;
+
+ if ( document->singledoc_name )
+ {
+ size += strlen( document->singledoc_name );
+ }
+ else
+ {
+ size += strlen( default_name );
+ }
+ size++; /* and the '\0'; */
+ size += RB_Get_Len_Extension( document->extension );
+
+ name = ( char * ) calloc( size, sizeof( char ) );
+ assert( name );
+ if ( document->singledoc_name )
+ {
+ strcat( name, document->singledoc_name );
+ }
+ else
+ {
+ strcat( name, default_name );
+ }
+ RB_Add_Extension( document->extension, name );
+
+ file = fopen( name, "w" );
+ if ( file )
+ {
+ /* File opened */
+ }
+ else
+ {
+ RB_Panic( "Can't open %s\n", name );
+ }
+ free( name );
+ return file;
+}
+
+/****/
+
+/****f* Document/RB_Get_RB_Document
+ * FUNCTION
+ * Allocate and initialize an RB_Document structure.
+ * SYNOPSIS
+ */
+struct RB_Document *RB_Get_RB_Document(
+ void )
+/*
+ * RESULT
+ * An initialized document structure.
+ * SOURCE
+ */
+{
+ struct RB_Document *document = 0;
+ document =
+ ( struct RB_Document * ) malloc( sizeof( struct RB_Document ) );
+ if ( document )
+ {
+ document->cur_part = NULL;
+ document->parts = NULL;
+ document->links = NULL;
+ document->headers = NULL;
+ document->doctype = UNKNOWN;
+ document->actions = No_Actions();
+ document->srctree = NULL;
+ document->srcroot = NULL;
+ document->docroot = NULL;
+ document->singledoc_name = NULL;
+ document->no_headers = 0;
+ document->charset = NULL;
+ document->extension = NULL;
+ document->first_section_level = 1;
+ document->doctype_name = NULL;
+ document->doctype_location = NULL;
+ }
+ else
+ {
+ RB_Panic( "out of memory" );
+ }
+ return document;
+}
+
+/*******/
--- /dev/null
+#ifndef ROBODOC_DOCUMENT_H
+#define ROBODOC_DOCUMENT_H
+// vi: spell ff=unix
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <stdio.h>
+#include "robodoc.h"
+
+/****s* Document/RB_Document
+ * NAME
+ * RB_Document -- Information store.
+ * FUNCTION
+ * A document is a collection of source files and documentation
+ * files. Depending on the mode that is used there is either a
+ * single documentation file or there is one documentation file for
+ * each source file. This mapping is stored in RB_Document. For
+ * each source file there is an RB_Part. It points to the source
+ * file, the documentation file, and contains all the headers that
+ * were found in the source file.
+ *
+ * ATTRIBUTES
+ * * links -- linked list of all links.
+ * * parts -- linked list of all parts.
+ * * no_headers -- total number of headers
+ * * headers -- array of pointers to all the headers.
+ * * srctree -- the list of all sourcefiles in the srcroot.
+ * * doctype -- the kind of documentation to be generated.
+ * * actions -- what to de while analysing en generating.
+ * * srcroot -- root dir for the sourcecode.
+ * * docroot -- root dir for the documentation.
+ * * charset -- the character set used for HTML and XML
+ * documentation.
+ * * first_section_level -- level of the first section,
+ * Defaults to 1 so the first section will
+ * be 1.
+ * If set to 2 the first section will be 1.1
+ * * extension -- the extension used for the documentation
+ * files.
+ * * css -- the cascading style sheet to be used.
+ * * cur_part -- unused TODO remove.
+ * SOURCE
+ */
+
+struct RB_Document
+{
+ struct RB_Part *cur_part;
+ struct RB_Part *parts;
+ struct RB_link *links;
+ unsigned long no_headers;
+ struct RB_header **headers;
+ struct RB_Directory *srctree;
+ T_RB_DocType doctype; /* HTML RTF etc */
+ actions_t actions;
+ int first_section_level; /* TODO document use of first_section_level in manual */
+ long debugmode; /* TODO This should not be in document */
+ char *singledoc_name;
+ struct RB_Path *srcroot; /* TODO Better make this a char* */
+ struct RB_Path *docroot; /* TODO Better make this a char* */
+ char *charset; /* HTML, XML? */
+ char *css;
+ char *extension;
+ char *compress;
+ char *section;
+ /* Docbook specific */
+ char *doctype_name; /* name part of the <!DOCTYPE> to be used with docbook output */
+ char *doctype_location;/* location part of the <!DOCTYPE> to be used with docbook output */
+};
+
+/*****/
+
+struct RB_Document *RB_Get_RB_Document(
+ void );
+void RB_Free_RB_Document(
+ struct RB_Document *document );
+void RB_Document_Add_Part(
+ struct RB_Document *document,
+ struct RB_Part *part );
+void RB_Document_Dump(
+ struct RB_Document *document );
+void RB_Document_Determine_DocFilePaths(
+ struct RB_Document *document );
+void RB_Document_Determine_DocFileNames(
+ struct RB_Document *document );
+void RB_Document_Create_DocFilePaths(
+ struct RB_Document *document );
+FILE *RB_Open_SingleDocumentation(
+ struct RB_Document *document );
+void RB_Document_Create_Parts(
+ struct RB_Document *document );
+void RB_Document_Collect_Headers(
+ struct RB_Document *document );
+void RB_Document_Link_Headers(
+ struct RB_Document *document );
+void RB_Fill_Header_Filename(
+ struct RB_Document *document );
+
+struct RB_header *RB_Document_Check_For_Duplicate(
+ struct RB_Document *arg_document,
+ struct RB_header *hdr );
+
+void RB_Document_Sort_Headers(
+ struct RB_Document *document );
+
+void RB_Document_Split_Parts(
+ struct RB_Document *document );
+
+#endif /* ROBODOC_DOCUMENT_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "file.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+#include "util.h"
+
+/****h* ROBODoc/Filename
+ * NAME
+ * Functions to deal with keeping track
+ * of filenames and directory names.
+ *****
+ */
+
+/****f* Filename/RB_Get_RB_Filename
+ * NAME
+ * RB_Get_RB_Filename
+ * SYNOPSIS
+ */
+struct RB_Filename* RB_Get_RB_Filename( char *arg_filename, struct RB_Path *arg_rb_path )
+/*
+ * INPUTS
+ * o arg_rb_filename --
+ * o arg_rb_path --
+ * FUNCTION
+ * Create a new RB_Filename structure based on arg_filename and
+ * arg_rb_path.
+ * SOURCE
+ */
+{
+ struct RB_Filename *rb_filename =
+ ( struct RB_Filename * ) malloc( sizeof( struct RB_Filename ) );
+ rb_filename->name = ( char * ) malloc( strlen( arg_filename ) + 1 );
+ rb_filename->docname = 0;
+ rb_filename->fullname = 0;
+ rb_filename->fulldocname = 0;
+ strcpy( rb_filename->name, arg_filename );
+ rb_filename->path = arg_rb_path;
+ return rb_filename;
+}
+
+/*****/
+
+struct RB_Filename *RB_Copy_RB_Filename( struct RB_Filename* arg_rb_filename )
+{
+ return RB_Get_RB_Filename( arg_rb_filename->name, arg_rb_filename->path );
+}
+
+
+void
+RB_Filename_Dump( struct RB_Filename *arg_rb_filename )
+{
+ printf( "[%s %s %s] ", RB_Get_Path( arg_rb_filename ),
+ RB_Get_Filename( arg_rb_filename ),
+ RB_Get_Extension( arg_rb_filename ) );
+ printf( "%s\n", Get_Fullname( arg_rb_filename ) );
+}
+
+/*x**f* Filename/RB_Free_RB_Filename
+ * NAME
+ * RB_Free_RB_Filename -- free a RB_Filename structure.
+ *
+ *****
+ * TODO Documentation
+ */
+
+void
+RB_Free_RB_Filename( struct RB_Filename *arg_rb_filename )
+{
+ free( arg_rb_filename->name );
+ if ( arg_rb_filename->docname )
+ {
+ free( arg_rb_filename->docname );
+ }
+ if ( arg_rb_filename->fullname )
+ {
+ free( arg_rb_filename->fullname );
+ }
+ if ( arg_rb_filename->fulldocname )
+ {
+ free( arg_rb_filename->fulldocname );
+ }
+ free( arg_rb_filename );
+}
+
+/* Set the fulldoc name, this is used in singledoc mode
+ * since there the docname is preset by the user and not
+ * derived from the sourcefile name.
+ */
+
+void RB_Set_FullDocname( struct RB_Filename *arg_rb_filename, char* name )
+{
+ arg_rb_filename->fulldocname = RB_StrDup( name );
+}
+
+/* TODO Documentation RB_Get_FullDocname */
+char *
+RB_Get_FullDocname( struct RB_Filename *arg_rb_filename )
+{
+ char *result = arg_rb_filename->fulldocname;
+
+ if ( result == NULL )
+ {
+ unsigned int size = strlen( arg_rb_filename->docname ) +
+ strlen( arg_rb_filename->path->docname ) + 1;
+ result = ( char * ) malloc( size * sizeof( char ) );
+ assert( result );
+ *result = '\0';
+ strcat( result, arg_rb_filename->path->docname );
+ strcat( result, arg_rb_filename->docname );
+ /* Save the result so it can be reused later on, and we can properly deallocate it. */
+ arg_rb_filename->fulldocname = result;
+ }
+ return result;
+}
+
+
+/****f* Filename/Get_Fullname
+ * NAME
+ * Get_Fullname --
+ * SYNOPSIS
+ */
+char* Get_Fullname( struct RB_Filename *arg_rb_filename )
+/*
+ * FUNCTION
+ * Give the full name of the file, that is the name of
+ * the file including the extension and the path.
+ * The path can be relative or absolute.
+ * NOTE
+ * The string returned is owned by this function
+ * so don't change it.
+ * SOURCE
+ */
+{
+ char *result = arg_rb_filename->fullname;
+
+ if ( result == NULL )
+ {
+ unsigned int size = strlen( arg_rb_filename->name ) +
+ strlen( arg_rb_filename->path->name ) + 1;
+ result = ( char * ) malloc( size * sizeof( char ) );
+ assert( result );
+ *result = '\0';
+ strcat( result, arg_rb_filename->path->name );
+ strcat( result, arg_rb_filename->name );
+ /* Save the result so it can be reused later on, and we can properly deallocate it. */
+ arg_rb_filename->fullname = result;
+ }
+ return result;
+}
+/******/
+
+/****f* Filename/RB_Get_Path
+ * SYNOPSIS
+ */
+char* RB_Get_Path( struct RB_Filename *arg_rb_filename )
+/*
+ * FUNCTION
+ * Give the path for this file.
+ * NOTE
+ * The string returned is owned by this function
+ * so don't change it.
+ ******
+ */
+{
+ return arg_rb_filename->path->name;
+}
+
+
+/****f* Filename/RB_Get_Extension
+ * NAME
+ * RB_Get_Extension --
+ * FUNCTION
+ * Give the extension of this file. That is the part after
+ * the last '.' if there is any.
+ * SYNOPSIS
+ */
+char* RB_Get_Extension( struct RB_Filename *arg_rb_filename )
+/*
+ * RESULT
+ * pointer to the extension
+ * pointer to a '\0' if no extension was found.
+ * NOTE
+ * The string returned is owned by this function
+ * so don't change it.
+ * SOURCE
+ */
+{
+ char *c = arg_rb_filename->name;
+ int i = strlen( c );
+
+ for ( c += i; c != arg_rb_filename->name && ( *c != '.' ); --c )
+ {
+ /* Empty */
+ }
+ if ( *c == '.' )
+ {
+ ++c;
+ }
+ else
+ {
+ c = arg_rb_filename->name;
+ c += i;
+ }
+ return c;
+}
+/*****/
+
+/****f* Filename/RB_Get_Filename
+ * NAME
+ * RB_Get_Filename --
+ * FUNCTION
+ * Give the name of this file. That is the name
+ * of the file without its path but with the
+ * extension.
+ * SYNOPSIS
+ */
+char* RB_Get_Filename( struct RB_Filename *arg_rb_filename )
+/*
+ * RESULT
+ * pointer to the extension
+ * pointer to a '\0' if no extension was found.
+ * NOTE
+ * The string returned is owned by this function
+ * so don't change it.
+ ******
+ */
+{
+ return arg_rb_filename->name;
+}
--- /dev/null
+#ifndef ROBODOC_FILE_H
+#define ROBODOC_FILE_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <stdio.h>
+#include "path.h"
+#include "links.h"
+
+/****s* Filename/RB_Filename
+ * NAME
+ * RB_Filename --
+ * ATTRIBUTES
+ * * next pointer to the next RB_File.
+ * * name null terminated string with the name of the file,
+ * (Without the path, but including the extension).
+ * * fullname
+ * * path pointer to a RB_Path structure that holds
+ * the path for this file.
+ * * link The link used to represent this file while in multidoc
+ * mode.
+ * SOURCE
+ */
+
+struct RB_Filename
+{
+ struct RB_Filename *next;
+ char *name;
+ char *docname;
+ char *fullname;
+ char *fulldocname;
+ struct RB_Path *path;
+ struct RB_link *link;
+};
+
+/******/
+
+
+struct RB_Filename *RB_Get_RB_Filename(
+ char *arg_filename,
+ struct RB_Path *arg_rb_path );
+void RB_Free_RB_Filename(
+ struct RB_Filename *arg_rb_filename );
+
+/* */
+char *Get_Fullname(
+ struct RB_Filename *arg_rb_filename );
+void RB_Set_FullDocname(
+ struct RB_Filename *arg_rb_filename,
+ char *name );
+char *RB_Get_FullDocname(
+ struct RB_Filename *arg_rb_filename );
+char *RB_Get_Path(
+ struct RB_Filename *arg_rb_filename );
+char *RB_Get_Filename(
+ struct RB_Filename *arg_rb_filename );
+char *RB_Get_Extension(
+ struct RB_Filename *arg_rb_filename );
+struct RB_Filename *RB_Copy_RB_Filename(
+ struct RB_Filename *arg_rb_filename );
+
+/* */
+void RB_Filename_Dump(
+ struct RB_Filename *arg_rb_filename );
+
+
+#endif /* ROBODOC_FILE_H */
--- /dev/null
+// vi: spell ff=unix
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+
+/****h* ROBODoc/Generator
+ * FUNCTION
+ * This contains routines to generate the documentation from the
+ * headers collected from the source code. It contains
+ * functionality common for all document types (HTML, RTF etc).
+ * The specifics are handled in the modules for each of the several
+ * document types.
+ *
+ * The behaviour of many of the functions in this module are
+ * modified by the global output_mode.
+ *
+ * The general call sequence is as follows:
+ * RB_Generate_Documentation
+ * +> RB_Generate_SingleDoc
+ * +> RB_Generate_Part
+ * +> Generate_Header
+ * +> Generate_Item_Line
+ * +> Generate_Char
+ * BUGS
+ * o Confusing use of doctype and output mode.
+ * NOTES
+ * Might be a good idea to replace all the switch statements with
+ * function pointers.
+ * So instead of:
+ * switch (output_mode)
+ * {
+ * case HTML:
+ * RB_HTML_Generate_Doc_Start(dest_doc, src_name, name, toc);
+ * break;
+ * case LATEX:
+ * RB_LaTeX_Generate_Doc_Start(dest_doc, src_name, name, toc);
+ * break;
+ * case RTF:
+ * RB_RTF_Generate_Doc_Start(dest_doc, src_name, name, toc);
+ * break;
+ * case ASCII:
+ * RB_ASCII_Generate_Doc_Start(dest_doc, src_name, name, toc);
+ * break;
+ * case TROFF:
+ * RB_TROFF_Generate_Doc_Start(dest_doc, src_name, name, toc);
+ * break;
+ * default:
+ * break;
+ * }
+ * we will have
+ * (*rb_generate_doc_start)(dest_doc, src_name, name, toc);
+ *
+ * were the function pointers are initialized at program start based
+ * on the output mode.
+ *******
+ * $Id: generator.c,v 1.106 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "globals.h"
+#include "robodoc.h"
+#include "headers.h"
+#include "items.h"
+#include "util.h"
+#include "links.h"
+#include "generator.h"
+#include "document.h"
+#include "part.h"
+#include "file.h"
+#include "roboconfig.h"
+
+/* Generators */
+#include "html_generator.h"
+#include "latex_generator.h"
+#include "xmldocbook_generator.h"
+#include "rtf_generator.h"
+#include "troff_generator.h"
+#include "ascii_generator.h"
+#include "test_generator.h"
+
+/* TODO This should not be here.... */
+#include "analyser.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+/* Module functions */
+// static void RB_Generate_Empty_Item( FILE * dest_doc );
+
+static void Generate_Item_Line(
+ FILE *dest_doc,
+ char *line,
+ int item_type,
+ char *docname,
+ struct RB_header *fnames );
+void Generate_Char(
+ FILE *dest_doc,
+ int cur_char );
+static void Generate_Index(
+ struct RB_Document *document );
+
+static void Generate_Header(
+ FILE *f,
+ struct RB_Document *document,
+ struct RB_header *header,
+ char *docname );
+
+static void Generate_Begin_Paragraph(
+ FILE *dest_doc );
+static void Generate_End_Paragraph(
+ FILE *dest_doc );
+static void Format_Line(
+ FILE *dest_doc,
+ long format );
+static void Generate_Begin_Preformatted(
+ FILE *dest_doc,
+ int source );
+static void Generate_End_Preformatted(
+ FILE *dest_doc );
+static void Generate_Begin_List_Item(
+ FILE *dest_doc );
+static void Generate_End_List_Item(
+ FILE *dest_doc );
+static void Generate_Begin_List(
+ FILE *dest_doc );
+static void Generate_End_List(
+ FILE *dest_doc );
+static void Pipe_Line(
+ FILE *dest_doc,
+ char *arg_line );
+
+static char piping = FALSE;
+
+
+/* TODO Documentation */
+void RB_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_False_Link( dest_doc, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_False_Link( dest_doc, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_False_Link( dest_doc, name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_False_Link( dest_doc, name );
+ break;
+ case RTF:
+ RB_RTF_Generate_False_Link( dest_doc, name );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_False_Link( dest_doc, name );
+ break;
+ case TROFF:
+ RB_TROFF_Generate_False_Link( dest_doc, name );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/****f* Generator/RB_Generate_Item_Begin
+ * FUNCTION
+ * Generate the begin of an item. This should switch to some
+ * preformatted output mode, similar to HTML's <PRE>.
+ * SYNOPSIS
+ */
+void RB_Generate_Item_Begin(
+ FILE *dest_doc,
+ char *name )
+/*
+ * INPUTS
+ * dest_doc -- file to be written to
+ * output_mode -- global with the current output mode
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Item_Begin( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Item_Begin( dest_doc );
+ break;
+ case HTML:
+ RB_HTML_Generate_Item_Begin( dest_doc, name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Item_Begin( dest_doc );
+ break;
+ case RTF:
+ RB_RTF_Generate_Item_Begin( dest_doc );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Item_Begin( dest_doc );
+ break;
+ case TROFF:
+ /* nothing */
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/******/
+
+
+/****f* Generator/Generate_Label
+ * FUNCTION
+ * Generate a label that can be used for a link.
+ * For instance in HTML this is <a name="label">
+ * SYNOPSIS
+ */
+void Generate_Label(
+ FILE *dest_doc,
+ char *name )
+/*
+ * INPUTS
+ * * dest_doc -- file to be written to
+ * * name -- the label's name.
+ * * output_mode -- global with the current output mode
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Label( dest_doc, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Label( dest_doc, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_Label( dest_doc, name );
+ break;
+ case LATEX:
+ /* lowtexx 21.09.2005 11:33 */
+ RB_LaTeX_Generate_Label( dest_doc, name );
+ break;
+ case RTF:
+ RB_RTF_Generate_Label( dest_doc, name );
+ break;
+ case ASCII:
+ /* Doesn't apply */
+ break;
+ case TROFF:
+ /* Doesn't apply */
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_Item_End
+ * FUNCTION
+ * Generate the end of an item. This should switch back from the
+ * preformatted mode. So in HTML it generates the </PRE> of a <PRE>
+ * </PRE> pair.
+ * INPUTS
+ * * dest_doc -- file to be written to
+ * * output_mode -- global with the current output mode
+ * SOURCE
+ */
+
+void RB_Generate_Item_End(
+ FILE *dest_doc,
+ char *name )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Item_End( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Item_End( dest_doc );
+ break;
+ case HTML:
+ RB_HTML_Generate_Item_End( dest_doc, name );
+ break;
+ case LATEX:
+ if ( piping == TRUE )
+ {
+ fprintf( dest_doc, "\\begin{verbatim}\n" );
+ piping = FALSE;
+ }
+ RB_LaTeX_Generate_Item_End( dest_doc );
+ break;
+ case RTF:
+ RB_RTF_Generate_Item_End( dest_doc );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Item_End( dest_doc );
+ break;
+ case TROFF:
+ /* Doesn't apply */ break;
+ case UNKNOWN:
+
+ default:
+ assert( 0 );
+ }
+}
+
+/****/
+
+
+
+/****f Generator/RB_Get_Len_Extension
+ * FUNCTION
+ * Compute the length of the filename extension for
+ * the current document type.
+ *****
+ */
+
+size_t RB_Get_Len_Extension(
+ char *extension )
+{
+ size_t size = 0;
+
+ size = strlen( extension );
+ if ( *extension != '.' )
+ size++;
+ return size;
+}
+
+/****f* Generator/RB_Add_Extension
+ * FUNCTION
+ * Add an extension to the filename base based on on the current
+ * output mode.
+ * INPUTS
+ * * doctype -- output mode
+ * * name -- the name of the file without extension and with
+ * enough room left to add the extension.
+ * OUTPUT
+ * name -- the name of the file including the extension.
+ * SOURCE
+ */
+
+void RB_Add_Extension(
+ char *extension,
+ char *name )
+{
+ if ( *extension != '.' )
+ strcat( name, "." );
+ strcat( name, extension );
+}
+
+/******/
+
+/*x**f Generator/RB_Default_Len_Extension
+ * FUNCTION
+ * Returns default extension for
+ * the current document type.
+ *****
+ */
+
+char *RB_Get_Default_Extension(
+ T_RB_DocType doctype )
+{
+ char *extension = NULL;
+
+ switch ( doctype )
+ {
+ case TEST:
+ extension = RB_TEST_Get_Default_Extension( );
+ break;
+ case XMLDOCBOOK:
+ extension = RB_XMLDB_Get_Default_Extension( );
+ break;
+ case HTML:
+ extension = RB_HTML_Get_Default_Extension( );
+ break;
+ case LATEX:
+ extension = RB_LaTeX_Get_Default_Extension( );
+ break;
+ case RTF:
+ extension = RB_RTF_Get_Default_Extension( );
+ break;
+ case ASCII:
+ extension = RB_ASCII_Get_Default_Extension( );
+ break;
+ case TROFF:
+ extension = RB_TROFF_Get_Default_Extension( );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+ return extension;
+}
+
+
+/****f* Generator/RB_Generate_BeginSection
+ * FUNCTION
+ * Generate a section of level depth in the current output mode.
+ * This is used for the --sections option. Where each header is
+ * placed in a section based on the header hierarchy.
+ * INPUTS
+ * * dest_doc -- the destination file.
+ * * doctype -- document type
+ * * depth -- the level of the section
+ * * name -- the name of the section
+ * * header -- pointer to the header structure
+ * * output_mode -- global with the current output mode.
+ * SOURCE
+ */
+
+void RB_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_BeginSection( dest_doc, depth, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_BeginSection( dest_doc, depth, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_BeginSection( dest_doc, depth, name, header );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_BeginSection( dest_doc, depth, name, header );
+ break;
+ case RTF:
+ RB_RTF_Generate_BeginSection( dest_doc, depth, name );
+ break;
+ case TROFF:
+/* RB_TROFF_Generate_BeginSection( dest_doc, depth, name ); */
+ break;
+ case ASCII:
+ RB_ASCII_Generate_BeginSection( dest_doc, depth, name, header );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/*******/
+
+
+/****f* Generator/RB_Generate_EndSection
+ * FUNCTION
+ * Generate the end of a section base on the current output mode.
+ * The functions is used for the --section option.
+ * It closes a section in the current output mode.
+ * INPUTS
+ * * dest_doc -- the destination file.
+ * * doctype --
+ * * depth -- the level of the section
+ * * name -- the name of the section
+ * * output_mode -- global with the current output mode.
+ * SOURCE
+ */
+
+void RB_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case RTF:
+ RB_RTF_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case TROFF:
+ /* doesn't apply */
+ break;
+ case ASCII:
+ RB_HTML_Generate_EndSection( dest_doc, depth, name );
+ break;
+ case UNKNOWN:
+
+ default:
+ assert( 0 );
+ }
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_Index_Entry
+ * FUNCTION
+ * Generate an entry for an auto generated index. This works only
+ * for output modes that support this, LaTeX for instance. This
+ * has nothting to do with the master index.
+ * SYNOPSIS
+ */
+void RB_Generate_Index_Entry(
+ FILE *dest_doc,
+ T_RB_DocType doctype,
+ struct RB_header *header )
+/*
+ * INPUTS
+ * * dest_doc -- the destination file.
+ * * header -- pointer to the header the index entry is for.
+ * * output_mode -- global with the current output mode.
+ * SOURCE
+ */
+{
+ switch ( doctype )
+ {
+ case TEST:
+ /* TODO */
+ break;
+ case XMLDOCBOOK:
+ /* TODO */
+ break;
+ case HTML:
+ /* TODO */
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Index_Entry( dest_doc, header );
+ break;
+ case RTF:
+ /* TODO */
+ break;
+ case ASCII:
+ /* No index available */
+ break;
+ case TROFF:
+ /* No index available */
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/*******/
+
+
+/****f* Generator/RB_Generate_TOC_2
+ * FUNCTION
+ * Create a Table of Contents based on the headers found in
+ * _all_ source files. There is also a function to create
+ * a table of contents based on the headers found in a single
+ * source file RB_Generate_TOC_1
+ * SYNOPSIS
+ */
+void RB_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count,
+ struct RB_Part *owner,
+ char *dest_name )
+/*
+ * INPUTS
+ * * dest_doc -- the destination file.
+ * * headers -- an array of pointers to all the headers.
+ * * count -- the number of pointers in the array.
+ * * output_mode -- global with the current output mode.
+ * * owner -- The owner of the TOC. Only the headers that are owned
+ * by this owner are included in the TOC. Can be NULL,
+ * in which case all headers are included.
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ break;
+ case TROFF:
+ break;
+ case XMLDOCBOOK:
+ break;
+ case HTML:
+ RB_HTML_Generate_TOC_2( dest_doc, headers, count, owner, dest_name );
+ break;
+ case LATEX:
+ /* LaTeX has it's own mechanism for creating
+ * a table of content */
+ break;
+ case RTF:
+ RB_RTF_Generate_TOC_2( dest_doc, headers, count );
+ break;
+ case ASCII:
+ /* TODO: No TOC in ASCII mode */
+ break;
+ case UNKNOWN:
+
+ default:
+ assert( 0 );
+ }
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_Doc_Start
+ * NAME
+ * RB_Generate_Doc_Start -- Generate document header.
+ * SYNOPSIS
+ */
+void RB_Generate_Doc_Start(
+ struct RB_Document *document,
+ FILE *dest_doc,
+ char *src_name,
+ char *title,
+ char toc,
+ char *dest_name,
+ char *charset )
+/*
+ * FUNCTION
+ * Generates for depending on the output_mode the text that
+ * will be at the start of a document.
+ * Including the table of contents.
+ * INPUTS
+ * o dest_doc - pointer to the file to which the output will
+ * be written.
+ * o src_name - the name of the source file or directory.
+ * o name - the name of this file.
+ * o output_mode - global variable that indicates the output
+ * mode.
+ * o toc - generate table of contens
+ * SEE ALSO
+ * RB_Generate_Doc_End
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Doc_Start( dest_doc, src_name, title, toc );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Doc_Start( document, dest_doc, charset );
+ break;
+ case HTML:
+ RB_HTML_Generate_Doc_Start( dest_doc, src_name, title, dest_name,
+ charset );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Doc_Start( dest_doc, src_name, title, charset );
+ break;
+ case RTF:
+ RB_RTF_Generate_Doc_Start( dest_doc, src_name, title, toc );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Doc_Start( dest_doc, src_name, title, toc );
+ break;
+ case TROFF:
+ /* */ ;
+ break;
+ case UNKNOWN:
+ default:
+ ;
+ }
+}
+
+/***************/
+
+
+/****f* Generator/RB_Generate_Doc_End
+ * NAME
+ * RB_Generate_Doc_End -- generate document trailer.
+ * SYNOPSIS
+ */
+void RB_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name,
+ char *src_name )
+/*
+ * FUNCTION
+ * Generates for depending on the output_mode the text that
+ * will be at the end of a document.
+ * INPUTS
+ * o dest_doc - pointer to the file to which the output will
+ * be written.
+ * o name - the name of this file.
+ * o output_mode - global variable that indicates the output
+ * mode.
+ * NOTES
+ * Doesn't do anything with its arguments, but that might
+ * change in the future.
+ * BUGS
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Doc_End( dest_doc, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Doc_End( dest_doc, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_Doc_End( dest_doc, name, src_name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Doc_End( dest_doc, name );
+ break;
+ case RTF:
+ RB_RTF_Generate_Doc_End( dest_doc, name );
+ break;
+ case TROFF:
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+
+ default:
+ assert( 0 );
+ }
+}
+
+/************/
+
+
+/****f* Generator/RB_Generate_Header_Start [3.0h]
+ * NAME
+ * RB_Generate_Header_Start -- generate header start text.
+ * SYNOPSIS
+ */
+FILE *RB_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+/*
+ * FUNCTION
+ * Generates depending on the output_mode the text that
+ * will be at the end of each header.
+ * INPUTS
+ * o dest_doc - pointer to the file to which the output will
+ * be written.
+ * o cur_header - pointer to a RB_header structure.
+ * SEE ALSO
+ * RB_Generate_Header_End
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case HTML:
+ RB_HTML_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case RTF:
+ RB_RTF_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case TROFF:
+ dest_doc = RB_TROFF_Generate_Header_Start( dest_doc, cur_header );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+ return dest_doc;
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_Header_End [3.0h]
+ * NAME
+ * RB_Generate_Header_End
+ * SYNOPSIS
+ */
+void RB_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+/*
+ * FUNCTION
+ * Generates for depending on the output_mode the text that
+ * will be at the end of a header.
+ * This function is used if the option --section is _not_
+ * used.
+ * INPUTS
+ * o dest_doc - pointer to the file to which the output will
+ * be written.
+ * o cur_header - pointer to a RB_header structure.
+ * SEE ALSO
+ * RB_Generate_Header_Start, RB_Generate_EndSection,
+ * RB_Generate_BeginSection
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ { /* switch by *koessi */
+ case TEST:
+ RB_TEST_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case HTML:
+ RB_HTML_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case RTF:
+ RB_RTF_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case TROFF:
+ RB_TROFF_Generate_Header_End( dest_doc, cur_header );
+ break;
+ case UNKNOWN:
+ default:
+ break;
+ }
+}
+
+/*****/
+
+
+/****f* Generator/Generate_Item_Name [2.01]
+ * NAME
+ * Generate_Item_Name -- fast&easy
+ * SYNOPSIS
+ */
+void Generate_Item_Name(
+ FILE *dest_doc,
+ int item_type )
+/*
+ * FUNCTION
+ * write the item's name to the doc
+ * INPUTS
+ * o FILE* dest_doc -- destination file
+ * o int item_type -- the type of item
+ * AUTHOR
+ * Koessi
+ * NOTES
+ * uses globals: output_mode
+ * SOURCE
+ */
+{
+ char *name = configuration.items.names[item_type];
+
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Item_Name( dest_doc, name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Item_Name( dest_doc, name );
+ break;
+ case HTML:
+ RB_HTML_Generate_Item_Name( dest_doc, name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Item_Name( dest_doc, name );
+ break;
+ case RTF:
+ RB_RTF_Generate_Item_Name( dest_doc, name );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Item_Name( dest_doc, name );
+ break;
+ case TROFF:
+ RB_TROFF_Generate_Item_Name( dest_doc, name, item_type );
+ break;
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/*********/
+
+
+void Generate_Begin_Content(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_Begin_Content( dest_doc );
+ break;
+ default:
+ break;
+ }
+}
+
+void Generate_End_Content(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_End_Content( dest_doc );
+ break;
+ default:
+ break;
+ }
+}
+
+void Generate_Begin_Navigation(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_Begin_Navigation( dest_doc );
+ break;
+ default:
+ break;
+ }
+
+}
+
+void Generate_End_Navigation(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_End_Navigation( dest_doc );
+ break;
+ default:
+ break;
+ }
+
+}
+
+void Generate_IndexMenu(
+ FILE *dest_doc,
+ char *filename,
+ struct RB_Document *document )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ RB_HTML_Generate_IndexMenu( dest_doc, filename, document, NULL );
+ break;
+ default:
+ break;
+ }
+
+}
+
+void Generate_Begin_Extra(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_Begin_Extra( dest_doc );
+ break;
+ default:
+ break;
+ }
+}
+
+void Generate_End_Extra(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ HTML_Generate_End_Extra( dest_doc );
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+
+
+
+/* TODO Documentation */
+void RB_Generate_Nav_Bar(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ break;
+ case XMLDOCBOOK:
+ break;
+ case HTML:
+ if ( course_of_action.do_one_file_per_header )
+ {
+ /* Nothing */
+ }
+ else
+ {
+ RB_HTML_Generate_Nav_Bar( document, current_doc, current_header );
+ }
+ break;
+ case LATEX:
+ break;
+ case RTF:
+ break;
+ case ASCII:
+ break;
+ case TROFF:
+ break;
+ case UNKNOWN:
+ default:
+ /* Bug */
+ assert( 0 );
+ }
+}
+
+
+
+/* TODO Documentation */
+int RB_HTML_Extra(
+ FILE *dest_doc,
+ int item_type,
+ char *cur_char,
+ char prev_char )
+{
+ int res = -1;
+
+ switch ( output_mode )
+ {
+ case TEST:
+ break;
+ case XMLDOCBOOK:
+ break;
+ case HTML:
+ res =
+ RB_HTML_Generate_Extra( dest_doc, item_type, cur_char,
+ prev_char );
+ break;
+ case LATEX:
+ /* TODO */
+ break;
+ case RTF:
+ /* TODO */
+ break;
+ case ASCII:
+ /* TODO */
+ break;
+ case TROFF:
+ res = RB_TROFF_Generate_Extra( dest_doc, item_type, cur_char );
+ break;
+ break;
+ case UNKNOWN:
+ default:
+ /* Bug */
+ assert( 0 );
+ }
+ return res;
+}
+
+
+/****f* Generator/RB_Name_Headers
+ * FUNCTION
+ * Give all headers a unique name. This makes sure that if
+ * two headers have the same name linking to one of the headers
+ * still works.
+ * SYNOPSIS
+ */
+void RB_Name_Headers(
+ struct RB_header **headers,
+ long count )
+/*
+ * SOURCE
+ */
+#define MAX_UNIQUE_ID_LENGTH 80
+{
+ int i;
+ char id[MAX_UNIQUE_ID_LENGTH + 1];
+
+ RB_Say( "Assigning headers a unique name.\n", SAY_DEBUG );
+ for ( i = 0; i < count; ++i )
+ {
+ struct RB_header *header;
+
+ header = headers[i];
+ sprintf( id, "robo%d", i );
+ header->unique_name = RB_StrDup( id );
+ }
+}
+
+/******/
+
+/****f* Generator/RB_Sort_Items
+ * FUNCTION
+ * Sort the items in all the headers according to the order
+ * specified in the 'item order' block in the robodoc.rc
+ * file.
+ * SYNOPSIS
+ */
+static void RB_Sort_Items(
+ struct RB_header **headers,
+ long header_count )
+/*
+ * SOURCE
+ */
+{
+ int n_order = configuration.item_order.number;
+
+ if ( n_order )
+ {
+ int i = 0;
+ int j = 0;
+ int max_number_of_items = 0;
+ struct RB_Item **items = NULL;
+ struct RB_Item **items_sorted = NULL;
+
+ RB_Say( "Sorting items in %d headers.\n", SAY_DEBUG, header_count );
+
+ /* Compute the maximum number of items in any given header */
+ for ( j = 0; j < header_count; ++j )
+ {
+ struct RB_header *header;
+ struct RB_Item *item;
+ int item_count = 0;
+
+ header = headers[j];
+ for ( item = header->items; item; item = item->next )
+ {
+ ++item_count;
+ }
+
+ if ( item_count > max_number_of_items )
+ {
+ max_number_of_items = item_count;
+ }
+ }
+ /* Allocate an array for the items, this makes it easier to
+ * sort. */
+
+ RB_Say( "Largest header has %d items.\n", SAY_DEBUG,
+ max_number_of_items );
+
+ if ( max_number_of_items == 0 )
+ {
+ /* No items in any of the headers, do nothing */
+ }
+ else
+ {
+ items = calloc( max_number_of_items, sizeof( struct RB_Item * ) );
+ items_sorted =
+ calloc( max_number_of_items, sizeof( struct RB_Item * ) );
+
+ /* Sort items */
+ for ( j = 0; j < header_count; ++j )
+ {
+ struct RB_header *header = NULL;
+ struct RB_Item *item = NULL;
+ int item_index = 0;
+ int sorted_item_index = 0;
+ int item_count = 0;
+
+ header = headers[j];
+
+ /* Copy item pointers to array */
+ for ( item = header->items; item;
+ item = item->next, ++item_index )
+ {
+ items[item_index] = item;
+ items_sorted[item_index] = item;
+ };
+ item_count = item_index;
+
+ if ( item_count == 0 )
+ {
+ /* No items in this header, do nothing. */
+ }
+ else
+ {
+ assert( item_count <= max_number_of_items );
+
+ /* First copy the items in the order that is defined in
+ * item_order[] to sorted_items[] */
+ sorted_item_index = 0;
+ for ( i = 0; i < n_order; ++i )
+ {
+ for ( item_index = 0; item_index < item_count;
+ ++item_index )
+ {
+ if ( items[item_index] )
+ {
+ if ( strcmp
+ ( configuration.items.
+ names[items[item_index]->type],
+ configuration.item_order.names[i] ) ==
+ 0 )
+ {
+ /* copy to items_sorted */
+ items_sorted[sorted_item_index] =
+ items[item_index];
+ ++sorted_item_index;
+ items[item_index] = NULL;
+ }
+ }
+ }
+ }
+ /* Then copy the remaining items to items_sorted[] */
+ for ( item_index = 0; item_index < item_count;
+ ++item_index )
+ {
+ if ( items[item_index] )
+ {
+ /* copy to items_sorted */
+ items_sorted[sorted_item_index] =
+ items[item_index];
+ ++sorted_item_index;
+ items[item_index] = NULL;
+ }
+ }
+
+ assert( sorted_item_index == item_count );
+
+ /* Now copy the items in items_sorted[] back into the linked list in
+ * the header */
+ if ( item_count > 1 )
+ {
+ /* Chain all the items */
+ for ( item_index = 0; item_index < item_count - 1;
+ ++item_index )
+ {
+ items_sorted[item_index]->next =
+ items_sorted[item_index + 1];
+ }
+ }
+
+ assert( item_count > 0 );
+ items_sorted[item_count - 1]->next = NULL;
+ /* now link the first item to the header. */
+ header->items = items_sorted[0];
+ }
+ }
+
+ free( items_sorted );
+ free( items );
+ }
+ }
+
+ RB_Say( "Done sorting items in %d headers.\n", SAY_DEBUG, header_count );
+}
+
+/*******/
+
+
+/****f* Generator/RB_Generate_Documentation
+ * FUNCTION
+ * Generate the documentation for all the information contained in a
+ * RB_Document structure.
+ * SYNOPSIS
+ */
+void RB_Generate_Documentation(
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * document -- pointer to the RB_Document structure.
+ * SOURCE
+ */
+{
+ RB_SetCurrentFile( NULL );
+
+
+
+ if ( document->actions.do_singledoc )
+ {
+ RB_Generate_SingleDoc( document );
+ }
+ else if ( document->actions.do_multidoc )
+ {
+ RB_Generate_MultiDoc( document );
+ }
+ else if ( document->actions.do_singlefile )
+ {
+ RB_Generate_SingleDoc( document );
+ }
+}
+
+/*****/
+
+/****f* Generator/RB_Generate_MultiDoc
+ * FUNCTION
+ * Create documentation by creating a file for each
+ * individual source file that was scanned.
+ * SYNOPSIS
+ */
+void RB_Generate_MultiDoc(
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * document -- pointer to the RB_Document structure.
+ * SOURCE
+ */
+{
+ struct RB_Part *i_part;
+ FILE *document_file = NULL;
+
+ RB_Document_Determine_DocFilePaths( document );
+ RB_Document_Create_DocFilePaths( document );
+ if ( document->actions.do_one_file_per_header )
+ {
+ RB_Document_Split_Parts( document );
+ }
+ RB_Document_Determine_DocFileNames( document );
+ RB_Document_Collect_Headers( document );
+ if ( document->actions.do_nosort )
+ {
+ /* Nothing */
+ }
+ else
+ {
+ RB_Document_Sort_Headers( document );
+ }
+ RB_Document_Link_Headers( document );
+ RB_Fill_Header_Filename( document );
+ RB_Name_Headers( document->headers, document->no_headers );
+ RB_Sort_Items( document->headers, document->no_headers );
+ RB_CollectLinks( document, document->headers, document->no_headers );
+ if ( output_mode == HTML )
+ {
+ RB_Create_CSS( document );
+ }
+
+
+ for ( i_part = document->parts; i_part != NULL; i_part = i_part->next )
+ {
+
+ char *srcname = Get_Fullname( i_part->filename );
+ char *docname = RB_Get_FullDocname( i_part->filename );
+
+ /* Nothing found in this part, do not generate it */
+ if ( i_part->headers == 0 )
+ continue;
+
+
+ if ( output_mode != TROFF )
+ {
+ document_file = RB_Open_Documentation( i_part );
+ RB_Generate_Doc_Start( document,
+ document_file, srcname, srcname, 1,
+ docname, document->charset );
+
+ Generate_Begin_Navigation( document_file );
+ if ( document->actions.do_one_file_per_header )
+ {
+ RB_HTML_Generate_Nav_Bar_One_File_Per_Header( document,
+ document_file,
+ i_part->
+ headers );
+ }
+ else
+ {
+ Generate_IndexMenu( document_file, docname, document );
+ }
+ Generate_End_Navigation( document_file );
+
+ Generate_Begin_Content( document_file );
+
+ if ( ( document->actions.do_toc ) && document->no_headers )
+ {
+ RB_Generate_TOC_2( document_file,
+ document->headers, document->no_headers,
+ i_part, docname );
+ }
+ RB_Generate_Part( document_file, document, i_part );
+ Generate_End_Content( document_file );
+
+ RB_Generate_Doc_End( document_file, docname, srcname );
+ fclose( document_file );
+ }
+ else
+ {
+ RB_Generate_Part( document_file, document, i_part );
+ }
+ }
+
+ if ( document->actions.do_index )
+ {
+ Generate_Index( document );
+ }
+
+ RB_Free_Links( );
+}
+
+/*****/
+
+
+/****f* Generator/RB_Generate_SingleDoc
+ * FUNCTION
+ * Create documentation by creating a single file for all individual
+ * source file that were scanned.
+ *
+ * This function is called when the option --singledoc is used.
+ * Based on whether the option --sections is used this function then
+ * calls RB_Generate_Sections or RB_Generate_Part
+ * SYNOPSIS
+ */
+void RB_Generate_SingleDoc(
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * document -- pointer to the RB_Document structure.
+ * SOURCE
+ */
+{
+ FILE *document_file;
+ struct RB_Part *i_part;
+
+ RB_Document_Collect_Headers( document );
+ if ( document->actions.do_nosort )
+ {
+ /* Nothing */
+ }
+ else
+ {
+ RB_Document_Sort_Headers( document );
+ }
+ RB_Document_Link_Headers( document );
+ RB_Fill_Header_Filename( document );
+ RB_Name_Headers( document->headers, document->no_headers );
+ RB_Sort_Items( document->headers, document->no_headers );
+ RB_CollectLinks( document, document->headers, document->no_headers );
+
+ for ( i_part = document->parts; i_part != NULL; i_part = i_part->next )
+ {
+ RB_Set_FullDocname( i_part->filename, document->singledoc_name );
+ }
+
+ if ( output_mode == HTML )
+ {
+ RB_Create_CSS( document );
+ }
+
+ document_file = RB_Open_SingleDocumentation( document );
+ assert( document->parts->filename->name );
+
+
+ RB_Generate_Doc_Start( document,
+ document_file,
+ document->srcroot->name,
+ document->singledoc_name, 1,
+ document->singledoc_name, document->charset );
+
+ if ( ( document->actions.do_toc ) && document->no_headers )
+ {
+ RB_Generate_TOC_2( document_file,
+ document->headers, document->no_headers, NULL,
+ document->parts->filename->name );
+ }
+ if ( document->actions.do_sections )
+ {
+ RB_Generate_Sections( document_file, document );
+ }
+ else
+ {
+ for ( i_part = document->parts;
+ i_part != NULL; i_part = i_part->next )
+ {
+ RB_Generate_Part( document_file, document, i_part );
+ }
+ }
+
+ RB_Generate_Doc_End( document_file, "singledoc",
+ document->srcroot->name );
+ fclose( document_file );
+
+ RB_Free_Links( );
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_Sections
+ * FUNCTION
+ * Creates the documentation for all headers found in all source
+ * files. The order in which they are generated depends on the
+ * header hierarchy. First the top level header's documentation
+ * is generated then, the documentation for all it's childern, then
+ * the next top level header's documentation is generated.
+ * This is a recursive proces.
+ * The idea is to create something like:
+ * 1. Parentheader1
+ * 1.1 Child1
+ * 1.2 Child2
+ * 1.2.1 Child's child1
+ * 2. Parentheader2
+ * etc
+ * SYNOPSIS
+ */
+void RB_Generate_Sections(
+ FILE *document_file,
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * o document_file -- destination file.
+ * o document -- pointer to the RB_Document structure.
+ * SOURCE
+ */
+{
+ unsigned long i;
+ int depth = 1;
+ struct RB_header *header;
+
+ depth = document->first_section_level;
+
+ RB_Say( "Generating Sections\n", SAY_INFO );
+ for ( i = 0; i < document->no_headers; ++i )
+ {
+ header = ( document->headers )[i];
+ if ( header->parent )
+ {
+ /* This will be in one of the subsections */
+ }
+ else
+ {
+ RB_Generate_Section( document_file, header, document, depth );
+ }
+ }
+}
+
+/******/
+
+
+
+/****f* Generator/RB_Generate_Section
+ * FUNCTION
+ * Generate the documentation for a header and all
+ * its childern.
+ * INPUTS
+ * o document_file -- destination file
+ * o parent -- the parent of the header for which the documentation
+ * is to be generated.
+ * o document -- pointer to the RB_Document structure.
+ * o depth -- level of sectioning ( 1 1.1 1.1.1 etc)
+ * SYNOPSIS
+ */
+void RB_Generate_Section(
+ FILE *document_file,
+ struct RB_header *parent,
+ struct RB_Document *document,
+ int depth )
+/*
+ * NOTE
+ * This is a recursive function.
+ * SEE ALSO
+ * RB_Generate_Sections
+ * SOURCE
+ */
+{
+ unsigned long i;
+ struct RB_header *header;
+ char *headername;
+
+ // We pass either modulename/name or just the name
+ if ( course_of_action.do_sectionnameonly )
+ {
+ headername = parent->function_name;
+ }
+ else
+ {
+ headername = parent->name;
+ }
+
+ switch ( output_mode )
+ {
+ case XMLDOCBOOK:
+ {
+ RB_Generate_BeginSection( document_file, depth, headername,
+ parent );
+ /* Docbook output does not like the labels to be
+ * generated before the <section> part
+ */
+ Generate_Label( document_file, parent->unique_name );
+ Generate_Label( document_file, parent->name );
+ }
+ break;
+ /* lowtexx 21.09.2005 11:37 */
+ case LATEX:
+ {
+ RB_Generate_BeginSection( document_file, depth, headername,
+ parent );
+ /* We have to start section before labeling in latex */
+ Generate_Label( document_file, parent->unique_name );
+ Generate_Label( document_file, parent->name );
+ }
+ break;
+ /* --- */
+ default:
+ {
+ Generate_Label( document_file, parent->unique_name );
+ Generate_Label( document_file, parent->name );
+ RB_Generate_BeginSection( document_file, depth, headername,
+ parent );
+ }
+ break;
+ }
+
+ RB_Generate_Nav_Bar( document, document_file, parent );
+ RB_Generate_Index_Entry( document_file, document->doctype, parent );
+ Generate_Header( document_file, document, parent, "dummy" );
+ for ( i = 0; i < document->no_headers; ++i )
+ {
+ header = ( document->headers )[i];
+ if ( header->parent == parent )
+ {
+ RB_Generate_Section( document_file, header, document, depth + 1 );
+ }
+ else
+ {
+ /* Leeg */
+ }
+ }
+ RB_Generate_EndSection( document_file, depth, parent->name );
+}
+
+/******/
+
+
+
+/****f* Generator/RB_Generate_Part
+ * FUNCTION
+ * Generate the documention for all the headers found in a single
+ * source file.
+ * SYNOPSIS
+ */
+void RB_Generate_Part(
+ FILE *document_file,
+ struct RB_Document *document,
+ struct RB_Part *part )
+/*
+ * INPUTS
+ * * document_file -- The file were it stored.
+ * * document -- All the documentation.
+ * * part -- pointer to a RB_Part that contains all the headers found
+ * in a single source file.
+ * SOURCE
+ */
+{
+ struct RB_header *i_header;
+ char *docname = NULL;
+ char *srcname = Get_Fullname( part->filename );
+
+ RB_Say( "generating documentation for file \"%s\"\n", SAY_INFO, srcname );
+ if ( document->actions.do_singledoc )
+ {
+ docname = document->singledoc_name;
+ }
+ else if ( document->actions.do_multidoc )
+ {
+ docname = RB_Get_FullDocname( part->filename );
+ }
+ else if ( document->actions.do_singlefile )
+ {
+ docname = document->singledoc_name;
+ }
+ else
+ {
+ assert( 0 );
+ }
+
+ if ( output_mode == TROFF )
+ {
+ RB_TROFF_Set_Param( document->compress, document->section );
+ }
+
+
+ for ( i_header = part->headers; i_header; i_header = i_header->next )
+ {
+ RB_Say( "generating documentation for header \"%s\"\n", SAY_INFO,
+ i_header->name );
+ document_file = RB_Generate_Header_Start( document_file, i_header );
+ RB_Generate_Nav_Bar( document, document_file, i_header );
+ RB_Generate_Index_Entry( document_file, document->doctype, i_header );
+ Generate_Header( document_file, document, i_header, docname );
+ RB_Generate_Header_End( document_file, i_header );
+ }
+}
+
+/******/
+
+
+/****f* Generator/RB_Get_DOT_Type
+ * FUNCTION
+ * Returns the type of the DOT file for the given output mode
+ * SOURCE
+ */
+char *RB_Get_DOT_Type(
+ void )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ return DOT_HTML_TYPE;
+ break;
+
+ case LATEX:
+ return DOT_LATEX_TYPE;
+ break;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+/******/
+
+
+/****f* Generator/RB_Generate_DOT_Image_Link
+ * FUNCTION
+ * Generates the image link for the created dot graphics
+ * SOURCE
+ */
+void RB_Generate_DOT_Image_Link(
+ FILE *f,
+ int dot_nr,
+ char *dot_type )
+{
+ switch ( output_mode )
+ {
+ case HTML:
+ fprintf( f, "<img src=\"%s%d.%s\">\n", DOT_GRAPH_NAME, dot_nr,
+ dot_type );
+ break;
+
+ case LATEX:
+ {
+ char str[TEMP_BUF_SIZE];
+
+ // First we need to convert graph to PDF
+ // (maybe the user is using pdflatex)
+#if defined(RB_MSVC)
+ sprintf( str, "%s %s%d.%s", EPSTOPDF_NAME,
+ DOT_GRAPH_NAME, dot_nr, dot_type );
+#else
+ snprintf( str, sizeof( str ), "%s %s%d.%s", EPSTOPDF_NAME,
+ DOT_GRAPH_NAME, dot_nr, dot_type );
+#endif
+ system( str );
+
+ fprintf( f, "\\includegraphics{%s%d}\n", DOT_GRAPH_NAME, dot_nr );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/******/
+
+/****f* Generator/Generate_Item
+ * SYNOPSIS
+ */
+static void Generate_Item(
+ FILE *f,
+ struct RB_Document *document,
+ struct RB_header *header,
+ struct RB_Item *cur_item,
+ char *docname )
+ /*
+ * FUNCTION
+ * Generate the documentation for a single item.
+ * SOURCE
+ */
+{
+ static int dot_nr = 1;
+ int line_nr;
+ char *dot_type = NULL;
+ FILE *tool = NULL; // Pipe handler to the tool we use
+ enum ItemType item_type = cur_item->type;
+ char *name = configuration.items.names[item_type];
+
+
+ Generate_Item_Name( f, item_type );
+ RB_Generate_Item_Begin( f, name );
+ for ( line_nr = 0; line_nr < cur_item->no_lines; ++line_nr )
+ {
+ struct RB_Item_Line *item_line = cur_item->lines[line_nr];
+ char *line = item_line->line;
+
+ // Plain item lines
+ if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_PLAIN ) )
+ {
+ Format_Line( f, item_line->format );
+ Generate_Item_Line( f, line, item_type, docname, header );
+ }
+ // Last line
+ else if ( item_line->kind == ITEM_LINE_END )
+ {
+ Format_Line( f, item_line->format );
+ }
+ // Normal Pipes
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_PIPE ) )
+ {
+ Format_Line( f, item_line->format );
+ if ( item_line->pipe_mode == output_mode )
+ {
+ Pipe_Line( f, line );
+ }
+ }
+ // Tool start
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_TOOL_START ) )
+ {
+ Format_Line( f, item_line->format );
+
+ // Change to docdir
+ RB_Change_To_Docdir( document );
+
+ // Open pipe to tool
+ tool = Open_Pipe( line );
+
+ // Get back to working dir
+ RB_Change_Back_To_CWD( );
+ }
+ // Tool (or DOT) body
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_TOOL_BODY ) )
+ {
+ if ( tool != NULL )
+ {
+ fprintf( tool, "%s\n", line );
+ }
+ }
+ // Tool end
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_TOOL_END ) )
+ {
+ // Close pipe
+ Close_Pipe( tool );
+ tool = NULL;
+ }
+ // DOT start
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_DOT_START ) )
+ {
+ Format_Line( f, item_line->format );
+
+ // Get DOT file type
+ dot_type = RB_Get_DOT_Type( );
+
+ if ( dot_type )
+ {
+ char pipe_str[TEMP_BUF_SIZE];
+
+ // Change to docdir
+ RB_Change_To_Docdir( document );
+#if defined(RB_MSVC)
+ sprintf( pipe_str,
+ "%s -T%s -o%s%d.%s", dot_name,
+ dot_type, DOT_GRAPH_NAME, dot_nr, dot_type );
+#else
+ snprintf( pipe_str, sizeof( pipe_str ),
+ "%s -T%s -o%s%d.%s", dot_name,
+ dot_type, DOT_GRAPH_NAME, dot_nr, dot_type );
+#endif
+ tool = Open_Pipe( pipe_str );
+ }
+ }
+ // DOT end
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_DOT_END ) )
+ {
+ if ( tool )
+ {
+ // Close pipe
+ Close_Pipe( tool );
+ tool = NULL;
+
+ // Generate link to image
+ RB_Generate_DOT_Image_Link( f, dot_nr, dot_type );
+
+ // Get back to working dir
+ RB_Change_Back_To_CWD( );
+
+ // Increment dot file number
+ dot_nr++;
+ }
+ }
+ // DOT file include
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_DOT_FILE ) )
+ {
+ Format_Line( f, item_line->format );
+
+ // Get DOT file type
+ dot_type = RB_Get_DOT_Type( );
+
+ if ( dot_type )
+ {
+ char str[TEMP_BUF_SIZE];
+
+ // Change to docdir
+ RB_Change_To_Docdir( document );
+
+#if defined(RB_MSVC)
+ sprintf( str,
+ "%s -T%s \"%s/%s\" -o%s%d.%s", dot_name,
+ dot_type, RB_Get_Saved_CWD( ), line,
+ DOT_GRAPH_NAME, dot_nr, dot_type );
+#else
+ snprintf( str, sizeof( str ),
+ "%s -T%s \"%s/%s\" -o%s%d.%s", dot_name,
+ dot_type, RB_Get_Saved_CWD( ), line,
+ DOT_GRAPH_NAME, dot_nr, dot_type );
+#endif
+ system( str );
+
+ // Generate link to image
+ RB_Generate_DOT_Image_Link( f, dot_nr, dot_type );
+
+ // Get back to working dir
+ RB_Change_Back_To_CWD( );
+
+ // Increment dot file number
+ dot_nr++;
+ }
+ }
+ // Exec item
+ else if ( !Works_Like_SourceItem( item_type ) &&
+ ( item_line->kind == ITEM_LINE_EXEC ) )
+ {
+ Format_Line( f, item_line->format );
+
+ // Change to docdir
+ RB_Change_To_Docdir( document );
+
+ // Execute line
+ system( line );
+
+ // Get back to working dir
+ RB_Change_Back_To_CWD( );
+ }
+ // Source lines
+ else if ( Works_Like_SourceItem( item_type ) )
+ {
+ Format_Line( f, item_line->format );
+ Generate_Item_Line( f, line, item_type, docname, header );
+ }
+ else
+ {
+ /* This item line is ignored */
+ }
+ }
+ RB_Generate_Item_End( f, name );
+}
+
+/******/
+
+
+/****f* Generator/Generate_Header
+ * FUNCTION
+ * Generate the documentation for all the items found in
+ * a header except for any items specified in
+ * configuration.ignore_items.
+ * SYNOPSIS
+ */
+static void Generate_Header(
+ FILE *f,
+ struct RB_Document *document,
+ struct RB_header *header,
+ char *docname )
+/*
+ * INPUTS
+ * * f -- destination file
+ * * header -- header to be searched.
+ * * srcname -- name of the source file the header was found in.
+ * * document -- name of the documentation file.
+ * BUGS
+ * This skips the first item body if the first item name was
+ * not correctly spelled.
+ * SOURCE
+ */
+{
+ struct RB_Item *cur_item;
+
+ for ( cur_item = header->items; cur_item; cur_item = cur_item->next )
+ {
+ enum ItemType item_type = cur_item->type;
+ char *name = configuration.items.names[item_type];
+
+ if ( Is_Ignore_Item( name ) )
+ {
+ /* User does not want this item */
+ }
+ else if ( Works_Like_SourceItem( item_type )
+ && ( course_of_action.do_nosource ) )
+ {
+ /* User does not want source items */
+ }
+ else
+ {
+ Generate_Item( f, document, header, cur_item, docname );
+ }
+ }
+}
+
+/******/
+
+
+static void Pipe_Line(
+ FILE *dest_doc,
+ char *arg_line )
+{
+ char *cur_char = arg_line;
+
+ for ( ; *cur_char && ( *cur_char == ' ' || *cur_char == '\t' );
+ cur_char++ )
+ {
+ fputc( *cur_char, dest_doc );
+ }
+ fprintf( dest_doc, "%s%s", cur_char,
+ ( output_mode == RTF ? "\\line" : "\n" ) );
+}
+
+
+static void Format_Line(
+ FILE *dest_doc,
+ long format )
+{
+ if ( format & RBILA_END_LIST_ITEM )
+ {
+ Generate_End_List_Item( dest_doc );
+ }
+ if ( format & RBILA_END_LIST )
+ {
+ Generate_End_List( dest_doc );
+ }
+ if ( format & RBILA_END_PRE )
+ {
+ Generate_End_Preformatted( dest_doc );
+ }
+ if ( format & RBILA_BEGIN_PARAGRAPH )
+ {
+ Generate_Begin_Paragraph( dest_doc );
+ }
+ if ( format & RBILA_END_PARAGRAPH )
+ {
+ Generate_End_Paragraph( dest_doc );
+ }
+ if ( format & RBILA_BEGIN_PRE )
+ {
+ Generate_Begin_Preformatted( dest_doc,
+ ( format & RBILA_BEGIN_SOURCE ) );
+ }
+ if ( format & RBILA_BEGIN_LIST )
+ {
+ Generate_Begin_List( dest_doc );
+ }
+ if ( format & RBILA_BEGIN_LIST_ITEM )
+ {
+ Generate_Begin_List_Item( dest_doc );
+ }
+}
+
+
+
+static void Generate_Begin_List_Item(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_Begin_List_Item( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_Begin_List_Item( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_Begin_List_Item( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_Begin_List_Item( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_Begin_List_Item( dest_doc );
+ break;
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+static void Generate_End_List_Item(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_End_List_Item( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_End_List_Item( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_End_List_Item( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_End_List_Item( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_End_List_Item( dest_doc );
+ break;
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+static void Generate_Begin_List(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_Begin_List( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_Begin_List( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_Begin_List( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_Begin_List( dest_doc );
+ break;
+
+ case RTF:
+ case TROFF:
+ case ASCII:
+ break;
+
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+/* TODO FS Document */
+
+static void Generate_End_List(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_End_List( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_End_List( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_End_List( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_End_List( dest_doc );
+ break;
+
+ case RTF:
+ case TROFF:
+ case ASCII:
+ break;
+
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+/* TODO FS Document */
+
+static void Generate_Begin_Preformatted(
+ FILE *dest_doc,
+ int source )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_Begin_Preformatted( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_Begin_Preformatted( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_Begin_Preformatted( dest_doc, source );
+ break;
+ case LATEX:
+ LaTeX_Generate_Begin_Preformatted( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_Begin_Preformatted( dest_doc );
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+/* TODO FS Document */
+static void Generate_End_Preformatted(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_End_Preformatted( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_End_Preformatted( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_End_Preformatted( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_End_Preformatted( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_End_Preformatted( dest_doc );
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+/* TODO FS Document */
+static void Generate_End_Paragraph(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_End_Paragraph( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_End_Paragraph( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_End_Paragraph( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_End_Paragraph( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_End_Paragraph( dest_doc );
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+/* TODO FS Document */
+static void Generate_Begin_Paragraph(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ TEST_Generate_Begin_Paragraph( dest_doc );
+ break;
+ case XMLDOCBOOK:
+ XMLDB_Generate_Begin_Paragraph( dest_doc );
+ break;
+ case HTML:
+ HTML_Generate_Begin_Paragraph( dest_doc );
+ break;
+ case LATEX:
+ LaTeX_Generate_Begin_Paragraph( dest_doc );
+ break;
+ case RTF:
+ break;
+ case TROFF:
+ TROFF_Generate_Begin_Paragraph( dest_doc );
+ break;
+ case ASCII:
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+#if 0
+/*x**f* Generator/RB_Generate_Empty_Item
+ * FUNCTION
+ * Generate documentation for an item with an empty body.
+ * INPUTS
+ * dest_doc -- destination file.
+ ****
+ * TODO Documentation
+ */
+
+static void RB_Generate_Empty_Item(
+ FILE *dest_doc )
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ break;
+ case XMLDOCBOOK:
+ break;
+ case HTML:
+ RB_HTML_Generate_Empty_Item( dest_doc );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Empty_Item( dest_doc );
+ break;
+ case RTF:
+ RB_RTF_Generate_Empty_Item( dest_doc );
+ break;
+ case TROFF:
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Empty_Item( dest_doc );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+#endif
+
+
+
+static void Generate_Item_Line(
+ FILE *dest_doc,
+ char *line,
+ int item_type,
+ char *docname,
+ struct RB_header *fnames )
+{
+ char *cur_char = line;
+ char *object_name = NULL;
+ char *label_name = NULL;
+ char *file_name = NULL;
+
+ enum EIL_State
+ { SKIP_ALPHANUM, SKIP_SPACE, SEARCH_LINK_START_WORD, SEARCH_LINK }
+ state = SKIP_SPACE;
+
+ for ( ; *cur_char; cur_char++ )
+ {
+ char c = *cur_char;
+
+ /* This is a little statemachine to switch searching for
+ * links on and off. A link can be found for a word
+ * or for a word combined with some punctuation
+ * characters. A word is a string of alpha numeric
+ * characters (including a '_'), say FunctionAA_10_B
+ * All other characters are punctuation characters.
+ * We do not want links to start in the middle of a word,
+ * but they can start after or on a punctuation character.
+ * So for a link can start at
+ * Foo_bar::SnaFu
+ * ^ ^^^
+ * | |||
+ *
+ */
+
+ /* Move to the next state based on current character. */
+ switch ( state )
+ {
+ case SKIP_SPACE:
+ /* In this state we try to skip of a string of space
+ * characters until we find something else.
+ */
+ {
+ if ( utf8_ispunct( c ) )
+ {
+ /* we were in the process of skipping spaces,
+ * but now we found a non-space character.
+ * This might be the begin of a link, so
+ * switch to the search link state.
+ */
+ state = SEARCH_LINK;
+ }
+ else if ( utf8_isalnum( c ) || ( c == '_' ) )
+ {
+ state = SEARCH_LINK_START_WORD;
+ }
+ else
+ {
+ /* Links can only start with a none space character,
+ * so if the current charater is a space, we skip it.
+ */
+ }
+ break;
+ }
+ case SKIP_ALPHANUM:
+ /* In this state we skipping a string of alpha
+ * numeric charaters after that the first
+ * character in this string did not result in a
+ * link.
+ */
+ {
+ if ( utf8_isspace( c ) )
+ {
+ /* We found the end of the string, so we go
+ * back to the space skipping state
+ */
+ state = SKIP_SPACE;
+ }
+ else if ( utf8_ispunct( c ) && ( c != '_' ) )
+ {
+ /* We found a puntuation character, this end
+ * the string of alpha numeric character, but
+ * can be the begin of a new link, so we
+ * switch to the seach link state.
+ */
+ state = SEARCH_LINK;
+ }
+ else
+ {
+ /* We stay in this state */
+ }
+ }
+ break;
+ case SEARCH_LINK_START_WORD:
+ /* In this state we are at the start of a string
+ * of alpha numeric characters.
+ */
+ {
+ if ( utf8_isalnum( c ) || ( c == '_' ) )
+ {
+ /* We are not at the second character of
+ * a string of alpha numeric characters,
+ * we can stop searching for links, as a
+ * link can only start at the begin of
+ * such a string.
+ */
+ state = SKIP_ALPHANUM;
+ }
+ else if ( utf8_ispunct( c ) && ( c != '_' ) )
+ {
+ state = SEARCH_LINK;
+ }
+ else if ( utf8_isspace( c ) )
+ {
+ state = SKIP_SPACE;
+ }
+ else
+ {
+ state = SKIP_SPACE;
+ }
+ }
+ break;
+ case SEARCH_LINK:
+ {
+ /* In this state we search for links. We stop
+ * searching if we encounter a space because this
+ * marks end of the word,
+ */
+ if ( utf8_isalnum( c ) || ( c == '_' ) )
+ {
+ /* We are at the start of a word.
+ */
+ state = SEARCH_LINK_START_WORD;
+ }
+ else if ( utf8_isspace( c ) )
+ {
+ state = SKIP_SPACE;
+ }
+ else
+ {
+ /* We stay in this state */
+ }
+ }
+ break;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ if ( ( ( state == SEARCH_LINK ) ||
+ ( state == SEARCH_LINK_START_WORD ) ) &&
+ Find_Link( cur_char, &object_name, &label_name, &file_name ) )
+ {
+ /* We found a link, so we can stop searching for one
+ * for now.
+ */
+ state = SKIP_SPACE;
+
+ if ( object_name && fnames->no_names > 0 )
+ {
+ extern char *function_name(
+ char * );
+ int i;
+
+ for ( i = 0; i < fnames->no_names; i++ )
+ if ( strcmp
+ ( object_name,
+ function_name( fnames->names[i] ) ) == 0 )
+ break;
+ if ( i < fnames->no_names )
+ {
+ RB_Generate_False_Link( dest_doc, object_name );
+ cur_char += strlen( object_name ) - 1;
+ }
+ else
+ {
+ Generate_Link( dest_doc, docname,
+ file_name, label_name, object_name );
+ cur_char += strlen( object_name ) - 1;
+ }
+ }
+ else
+ {
+ assert( 0 );
+ }
+ }
+ else
+ {
+ int res =
+ RB_HTML_Extra( dest_doc, item_type, cur_char,
+ ( cur_char == line ) ? 0 : *( cur_char - 1 ) );
+
+ if ( res >= 0 )
+ {
+ cur_char += res;
+ }
+ else
+ {
+ /* convert from signed to unsigned */
+ unsigned char c2 = *cur_char;
+
+ Generate_Char( dest_doc, c2 );
+ }
+ }
+ }
+
+ /* TODO Move to the RTF_Generator */
+ switch ( output_mode )
+ {
+ case RTF:
+ fprintf( dest_doc, "\\line" );
+ break;
+
+ case TROFF:
+ RB_TROFF_Start_New_Line( dest_doc );
+ break;
+
+ case HTML:
+ // Check for source line comment endings
+ RB_HTML_Generate_Line_Comment_End( dest_doc );
+ break;
+
+ case TEST:
+ case ASCII:
+ case LATEX:
+ case XMLDOCBOOK:
+ case UNKNOWN:
+ break;
+ default:
+ break;
+ }
+
+ /* For all modes we add a newline. */
+ fputc( '\n', dest_doc );
+}
+
+
+
+/****f* Generator/Generate_Link
+ * FUNCTION
+ * Generate a link to another headers documentation.
+ * SYNOPSIS
+ */
+void Generate_Link(
+ FILE *dest_doc,
+ char *docname,
+ char *file_name,
+ char *label_name,
+ char *function_name )
+/*
+ * INPUTS
+ * * dest_doc -- the output file
+ * * docname -- the name of the output file
+ * * file_name -- the name of the file that contains the link's body
+ * * label_name -- the label for the link
+ * * function_name -- the name that is shown for the link in the
+ * documentation
+ * SOURCE
+ */
+{
+
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Link( dest_doc, docname, file_name, label_name,
+ function_name );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Link( dest_doc, docname, file_name, label_name,
+ function_name );
+ break;
+ case HTML:
+ RB_HTML_Generate_Link( dest_doc, docname, file_name, label_name,
+ function_name, NULL );
+ break;
+ case RTF:
+ RB_RTF_Generate_Link( dest_doc, docname, file_name, label_name,
+ function_name );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_String( dest_doc, function_name );
+ /* lowtexx 21.09.2005 11:43 */
+ /* generate a simple link here */
+ RB_LaTeX_Generate_Link( dest_doc, docname, file_name, label_name,
+ function_name );
+ break;
+ case UNKNOWN:
+ case ASCII:
+ case TROFF:
+ default:
+ fprintf( dest_doc, "%s", function_name );
+ }
+}
+
+/******/
+
+
+/****f* Generator/Generate_Char
+ * FUNCTION
+ * Generate a single character in the current output mode.
+ * The individual generators will make sure that special
+ * characters are escaped.
+ * SYNOPSIS
+ */
+void Generate_Char(
+ FILE *dest_doc,
+ int cur_char )
+/*
+ * INPUTS
+ * * dest_doc -- destination file.
+ * * cur_char -- character to be generated.
+ *******
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ RB_TEST_Generate_Char( dest_doc, cur_char );
+ break;
+ case XMLDOCBOOK:
+ RB_XMLDB_Generate_Char( dest_doc, cur_char );
+ break;
+ case HTML:
+ RB_HTML_Generate_Char( dest_doc, cur_char );
+ break;
+ case LATEX:
+ RB_LaTeX_Generate_Char( dest_doc, cur_char );
+ break;
+ case RTF:
+ RB_RTF_Generate_Char( dest_doc, cur_char );
+ break;
+ case TROFF:
+ RB_TROFF_Generate_Char( dest_doc, cur_char );
+ break;
+ case ASCII:
+ RB_ASCII_Generate_Char( dest_doc, cur_char );
+ break;
+ case UNKNOWN:
+ default:
+ assert( 0 );
+ }
+}
+
+
+/****f* Generator/RB_Get_SubIndex_FileName
+ * FUNCTION
+ * Get the name of the master index file for a specific
+ * header_type.
+ * SYNOPSIS
+ */
+char *RB_Get_SubIndex_FileName(
+ char *docroot,
+ char *extension,
+ struct RB_HeaderType *header_type )
+/*
+ * INPUTS
+ * * docroot -- the path to the documentation directory.
+ * * extension -- the extension for the file
+ * * header_type -- the header type
+ * RESULT
+ * a pointer to a freshly allocated string.
+ * NOTES
+ * Has too many parameters.
+ * SOURCE
+ */
+{
+ size_t l;
+ char *filename;
+
+ assert( docroot );
+
+ l = strlen( docroot );
+
+ l += RB_Get_Len_Extension( extension );
+ l += strlen( docroot );
+ l += strlen( header_type->fileName );
+
+ filename = ( char * ) malloc( l + 2 );
+ assert( filename );
+ filename[0] = '\0';
+
+ strcat( filename, docroot );
+ strcat( filename, header_type->fileName );
+ RB_Add_Extension( extension, filename );
+
+ return filename;
+}
+
+/*****/
+
+
+/****f* Generator/Generate_Index
+ * NAME
+ * Generate_Index -- generate index file based on xref files.
+ * SYNOPSIS
+ */
+static void Generate_Index(
+ struct RB_Document *document )
+/*
+ * FUNCTION
+ * Create a master index file. It contains pointers to the
+ * documentation generated for each source file, as well as all
+ * "objects" found in the source files.
+ * SOURCE
+ */
+{
+ switch ( output_mode )
+ {
+ case TEST:
+ break;
+ case XMLDOCBOOK:
+ break;
+ case HTML:
+ RB_HTML_Generate_Index( document );
+ break;
+ case LATEX:
+ /* RB_LaTeX_Generate_Index(dest, source); */
+ /* Latex has a index by default */
+ break;
+ case UNKNOWN:
+ case ASCII:
+ case TROFF:
+ case RTF:
+ default:
+ break;
+ }
+}
+
+/*****/
--- /dev/null
+#ifndef ROBODOC_GENERATOR_H
+#define ROBODOC_GENERATOR_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "robodoc.h"
+#include "headers.h"
+#include "document.h"
+
+void RB_Generate_Doc_Start(
+ struct RB_Document *document,
+ FILE *dest_doc,
+ char *src_name,
+ char *title,
+ char toc,
+ char *dest_name,
+ char *charset );
+char *RB_Generate_Item_Body(
+ FILE *,
+ char *,
+ char *,
+ char *,
+ char *,
+ int,
+ int );
+void RB_Generate_Item_Name(
+ FILE *,
+ int );
+void RB_Generate_Doc_End(
+ FILE *,
+ char *,
+ char * );
+FILE *RB_Generate_Header_Start(
+ FILE *,
+ struct RB_header * );
+void RB_Generate_Header_End(
+ FILE *,
+ struct RB_header * );
+int RB_HTML_Extra(
+ FILE *dest_doc,
+ int item_type,
+ char *cur_char,
+ char prev_char );
+void RB_Generate_Index_Table(
+ FILE *dest,
+ int type,
+ char *source );
+void RB_Name_Headers(
+ struct RB_header **headers,
+ long count );
+void RB_Generate_Documentation(
+ struct RB_Document * );
+void RB_Generate_Part(
+ FILE *document_file,
+ struct RB_Document *document,
+ struct RB_Part *part );
+void RB_Generate_Header_NG(
+ FILE *f,
+ struct RB_Document *document,
+ struct RB_header *header,
+ char *srcname,
+ char *docname );
+char *RB_Get_SubIndex_FileName(
+ char *docroot,
+ char *extension,
+ struct RB_HeaderType *header_type );
+void RB_Add_Extension(
+ char *extension,
+ char *name );
+size_t RB_Get_Len_Extension(
+ char *extension );
+char *RB_Get_Default_Extension(
+ T_RB_DocType doctype );
+void RB_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header );
+void RB_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+void RB_Generate_Section(
+ FILE *document_file,
+ struct RB_header *parent,
+ struct RB_Document *document,
+ int depth );
+void RB_Generate_Sections(
+ FILE *document_file,
+ struct RB_Document *document );
+void RB_Generate_SingleDoc(
+ struct RB_Document *document );
+void RB_Generate_MultiDoc(
+ struct RB_Document *document );
+void Generate_Link(
+ FILE *dest_doc,
+ char *docname,
+ char *file_name,
+ char *label_name,
+ char *function_name );
+void RB_Generate_Item_Begin(
+ FILE *dest_doc,
+ char *name );
+void RB_Generate_Item_End(
+ FILE *dest_doc,
+ char *name );
+void RB_Generate_Nav_Bar(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header );
+void RB_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count,
+ struct RB_Part *owner,
+ char *dest_name );
+
+
+#endif /* ROBODOC_GENERATOR_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/Globals
+ * FUNCTION
+ * A number of global variables.
+ * TODO
+ * Documentation.
+ *****
+ * $Id: globals.c,v 1.22 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "robodoc.h"
+#include "globals.h"
+#include "links.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+int number_of_warnings = 0;
+
+/* Pointer to the name of the current file that is being analysed,
+ use by RB_Panic */
+
+char *current_file = 0;
+
+/****v* Globals/document_title
+ * NAME
+ * documentat_title -- title for the documentation.
+ * PURPOSE
+ * Used as the title for master index files or for latex documentation.
+ * SOURCE
+ */
+
+char *document_title = NULL;
+
+/******/
+
+
+/****v* Globals/output_mode [2.0]
+ * NAME
+ * output_mode -- the mode of output
+ * FUNCTION
+ * Controls which type of output will be generated.
+ * SOURCE
+ */
+
+T_RB_DocType output_mode = ASCII;
+
+/*******/
+
+
+/****v* Globals/course_of_action [2.0]
+ * NAME
+ * course_of_action
+ * FUNCTION
+ * Global Variable that defines the course of action.
+ * SOURCE
+ */
+
+actions_t course_of_action;
+
+/*******/
+
+/****v* Globals/debugmode
+ * NAME
+ * debugmode
+ * FUNCTION
+ * A bitfield determining the output levels
+ * SOURCE
+ */
+
+long debugmode = 0;
+
+/*******/
+
+
+/****v* Globals/line_buffer [2.0]
+ * NAME
+ * line_buffer -- global line buffer
+ * FUNCTION
+ * Temporary storage area for lines
+ * that are read from an input file.
+ * SOURCE
+ */
+
+char line_buffer[MAX_LINE_LEN];
+
+/*******/
+
+/****v* Globals/myLine
+ * NAME
+ * myLine -- dynamic buffer for current line
+ * FUNCTION
+ * Temporary storage area for lines
+ * that are read from an input file.
+ * SOURCE
+ */
+
+char *myLine = NULL;
+
+/*******/
+
+/****v* Globals/readChars
+ * NAME
+ * readChars -- number of characters in the currently bufferd line
+ * FUNCTION
+ * Temporary storage area for lines
+ * that are read from an input file.
+ * SOURCE
+ */
+
+int readChars = 0;
+
+/*******/
+
+/****v* Globals/line_number [2.0]
+ * NAME
+ * line_number -- global line counter
+ * PURPOSE
+ * Keeps track of the number of lines that are read from the source file.
+ * AUTHOR
+ * Koessi
+ * SOURCE
+ */
+
+int line_number = 0;
+
+/*******/
+
+/*
+ * Global variables
+ */
+/* TODO Document these. */
+
+char *source_file; /* DCD */
+char *whoami = NULL; /* me,myself&i */
+int tab_stops[MAX_TABS]; /* Tab stop positions */
+
+// Number of header item names before linebreak
+int header_breaks = DEFAULT_HEADER_BREAKS;
+
+
+// Path and filename to dot tool
+char *dot_name = DEFAULT_DOT_NAME;
+
+
+/****i* Globals/RB_Close_The_Shop [3.0b]
+ * NAME
+ * RB_Close_The_Shop -- free resources.
+ * SYNOPSIS
+ * void RB_Close_The_Shop ()
+ * FUNCTION
+ * Frees all resources used by robodoc.
+ * SEE ALSO
+ * RB_Free_Header(), RB_Free_Link()
+ * SOURCE
+ */
+
+void RB_Close_The_Shop(
+ void )
+{
+/* TODO if (link_index) { free(link_index); } */
+ if ( myLine )
+ {
+ free( myLine );
+ }
+}
+
+/******/
--- /dev/null
+#ifndef ROBODOC_GLOBALS_H
+#define ROBODOC_GLOBALS_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <stdio.h>
+#include "robodoc.h"
+
+/* TODO Try to get rid of as many as these as possible. */
+
+#define MAX_LINE_LEN 512
+#define MAX_TABS 256
+#define DEFAULT_TABSIZE 8
+#define TABSIZE_SEPARATOR ","
+#define DEFAULT_HEADER_BREAKS 2
+#define MAX_HEADER_BREAKS 255
+
+#define TEMP_BUF_SIZE 1024
+#define DEFAULT_DOT_NAME "dot"
+#define DOT_GRAPH_NAME "dot_graph_"
+#define EPSTOPDF_NAME "epstopdf"
+#define DOT_HTML_TYPE "png"
+#define DOT_LATEX_TYPE "ps"
+
+// Class definitions for syntax highlighting
+// Note: Should correlate with css file
+#define SOURCE_CLASS "source"
+#define KEYWORD_CLASS "keyword"
+#define COMMENT_CLASS "comment"
+#define QUOTE_CLASS "quote"
+#define SQUOTE_CLASS "squote"
+#define SIGN_CLASS "sign"
+
+// Default HTML charset
+#define DEFAULT_CHARSET "ISO-8859-1"
+
+// Default document title
+#define DEFAULT_DOCTITILE "API Reference"
+
+// Document title
+extern char *document_title;
+
+extern char *source_file; /* DCD */
+
+/* extern T_RB_DocType output_mode; */
+/* extern long course_of_action; */
+/* extern int line_number; */
+void RB_Close_The_Shop(
+ void );
+
+// More ugly globals... Any idea where should we place them?
+extern int tab_stops[MAX_TABS];
+extern char *dot_name;
+extern int header_breaks;
+
+/* These are really necessary */
+extern int number_of_warnings;
+extern unsigned int link_index_size;
+extern struct RB_link **link_index;
+extern char *current_file;
+extern T_RB_DocType output_mode;
+extern actions_t course_of_action;
+extern int line_number;
+extern char line_buffer[MAX_LINE_LEN];
+extern char *whoami;
+extern char *myLine;
+extern int readChars;
+extern long debugmode;
+
+#endif /* ROBODOC_GLOBALS_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Headers
+ * FUNCTION
+ * This module contains a set of variables that define how headers
+ * start and how they end in various programming languages.
+ * NOTES
+ * Added C++/ACM header option (David White)
+ * Enables documentation only comments (//!) to be extracted from C++
+ * and ACM files, rather than all comments.
+ ******
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "robodoc.h"
+#include "headers.h"
+#include "globals.h"
+#include "roboconfig.h"
+#include "util.h"
+
+/* This limits the total number of possible markers. */
+/* TODO should be an enum */
+#define NO_MARKER_LOCKED 100000
+#define NO_MARKER 100002
+
+static int locked_header_marker = NO_MARKER_LOCKED;
+static int locked_end_marker = NO_MARKER_LOCKED;
+static int locked_remark_marker = NO_MARKER_LOCKED;
+
+/****v* Headers/header_markers
+ * NAME
+ * header_markers -- strings that mark the begin of a header.
+ * FUNCTION
+ * These specify what robodoc recognizes as the beginning
+ * NOTE
+ * The numbers at the beginning of the lines make it easier
+ * to keep them in sync with the src_constants
+ * of a header.
+ * SOURCE
+ */
+
+char *header_markers[] = {
+ "/****", /* 0 C, C++ */
+ "//!****", /* 1 C++, ACM */
+ "//****", /* 2 C++ */
+ "(****", /* 3 Pascal, Modula-2, B52 */
+ "{****", /* 4 Pascal */
+ ";;!****", /* 5 Aspen Plus */
+ ";****", /* 6 M68K assembler */
+ "****", /* 7 M68K assembler */
+ "C ****", /* 8 Fortran */
+ "REM ****", /* 9 BASIC */
+ "%****", /* 10 LaTeX, TeX, Postscript */
+ "#****", /* 11 Tcl/Tk */
+ " ****", /* 12 COBOL */
+ "--****", /* 13 Occam */
+ "<!--****", /* 14 HTML Code */
+ "<!---****", /* 15 HTML Code, the three-dashed comment
+ * tells the [server] pre-processor not
+ * to send that comment with the HTML
+ */
+ "|****", /* 16 GNU Assembler */
+ "!****", /* 17 FORTRAN 90 */
+ "!!****", /* 18 FORTRAN 90 */
+ "$!****", /* 19 DCL */
+ "'****", /* 20 VB, LotusScript */
+ ".****", /* 21 DB/C */
+ "\\ ****", /* 22 Forth */
+ "<!-- ****", /* 23 XML */
+ NULL
+};
+
+/****/
+
+/****v* Headers/robo_header
+ * NAME
+ * robo_header -- the distinct robodoc header -
+ * alternative to using header_markers
+ * FUNCTION
+ * This is an alternative to using header_markers - sometimes
+ * ROBODOC confuses asterisks commonly used in comments as a header.
+ * To use this header instead of header_markers use the -rh switch.
+ * NOTE
+ * Added by David Druffner. OBSOLETE
+ * SOURCE
+ */
+
+char *robo_header = "/*ROBODOC*"; /* TODO Remove */
+
+/****/
+
+
+/****v* Headers/remark_markers [3.0h]
+ * NAME
+ * remark_markers
+ * FUNCTION
+ * These specify what robodoc recognizes as a comment marker.
+ * TODO
+ * (1) All the markers that start with one or more spaces are
+ * never recognized, and should be removed.
+ * (2) The numbers at the beginning of the lines make it easier
+ * to keep them in sync with the src_remark_constants
+ * SOURCE
+ */
+
+char *remark_markers[] = {
+
+ " *", /* 0 C, C++, Pascal, Modula-2 */
+ "//!", /* 1 C++, ACM *//* MUST CHECK BEFORE C++ */
+ "//", /* 2 C++ */
+ "*", /* 3 C, C++, M68K assembler, Pascal, Modula-2 */
+ ";;!", /* 4 Aspen Plus *//* MUST CHECK BEFORE M68K */
+ ";*", /* 5 M68K assembler */
+ ";", /* 6 M68K assembler */
+ "C", /* 7 Fortran */
+ "REM", /* 8 BASIC */
+ "%", /* 9 LaTeX, TeX, Postscript */
+ "#", /* 10 Tcl/Tk */
+ " *", /* 11 COBOL */
+ "--", /* 12 Occam */
+ "|", /* 13 GNU Assembler */
+ "!!", /* 14 FORTRAN 90 */
+ "!", /* 15 FORTRAN 90 */
+ "$!", /* 16 DCL */
+ "'*", /* 17 VB */
+ ".*", /* 18 DB/C */
+ "\\", /* 19 Forth */
+ NULL
+};
+
+/****/
+
+
+/****v* Headers/end_markers [3.0h]
+ * NAME
+ * end_markers -- strings that mark the end of a header.
+ * FUNCTION
+ * These specify what robodoc recognizes as the end of a
+ * documentation header. In most cases this will be
+ * "***" or " ***". If the header contains a SOURCE item
+ * then the end of the source has to be marked, which
+ * is when the other strings in this array are used.
+ * NOTE
+ * The numbers at the beginning of the lines make it easier
+ * to find a special index-number.
+ * SOURCE
+ */
+
+char *end_markers[] = {
+ "/***", /* 0 C, C++ */
+ "//!***", /* 1 C++, ACM *//* Must check before C++ */
+ "//***", /* 2 C++ */
+ " ***", /* 3 C, C++, Pascal, Modula-2 */
+ "{***", /* 4 Pascal */
+ "(***", /* 5 Pascal, Modula-2, B52 */
+ ";;!***", /* 6 Aspen Plus *//* Must check before M68K */
+ ";***", /* 7 M68K assembler */
+ "***", /* 8 M68K assembler */
+ "C ***", /* 9 Fortran */
+ "REM ***", /* 10 BASIC */
+ "%***", /* 11 LaTeX, TeX, Postscript */
+ "#***", /* 12 Tcl/Tk */
+ " ***", /* 13 COBOL */
+ "--***", /* 14 Occam */
+ "<!--***", /* 15 HTML */
+ "<!---***", /* 16 HTML */
+ "|***", /* 17 GNU Assembler */
+ "!!***", /* 18 FORTRAN 90 */
+ "!***", /* 19 FORTRAN 90 */
+ "$!***", /* 20 DCL */
+ "'***", /* 21 VB, LotusScript */
+ ".***", /* 22 DB/C */
+ "\\ ***", /* 23 Forth */
+ "<!-- ***", /* 24 XML */
+ NULL
+};
+
+/****/
+
+/****v* Headers/end_remark_markers
+ * NAME
+ * end_remark_markers -- strings that mark the end of a comment.
+ * NOTE
+ * The numbers at the beginning of the lines make it easier
+ * to keep them in sync with the end_remark_constants
+ * SOURCE
+ */
+
+char *end_remark_markers[] = {
+ "*/", /* 0 C, C++ */
+ ( char * ) NULL, /* 1 C++, ACM */
+ ( char * ) NULL, /* 2 C++ */
+ ( char * ) NULL, /* 3 C, C++, Pascal, Modula-2 */
+ "***}", /* 5 Pascal */
+ "***)", /* 6 Pascal, Modula-2, B52 */
+ ( char * ) NULL, /* 7 M68K assembler */
+ ( char * ) NULL, /* 8 M68K assembler */
+ ( char * ) NULL, /* 9 Fortran */
+ ( char * ) NULL, /* 10 BASIC */
+ ( char * ) NULL, /* 11 LaTeX, TeX, Postscript */
+ ( char * ) NULL, /* 12 Tcl/Tk */
+ ( char * ) NULL, /* 13 COBOL */
+ ( char * ) NULL, /* 14 Occam */
+ "-->", /* 15 HTML & XML */
+ "--->", /* 16 HTML */
+ ( char * ) NULL, /* 17 GNU Assembler */
+ ( char * ) NULL, /* 18 FORTRAN 90 !! */
+ ( char * ) NULL, /* 19 FORTRAN 90 ! */
+ ( char * ) NULL, /* 20 VB */
+ ( char * ) NULL, /* 21 Aspen Plus */
+ ( char * ) NULL /* 22 Forth */
+};
+
+/****/
+
+
+/****v* Headers/robo_end [3.0h]
+ * NAME
+ * robo_end[] -- the distinct robodoc end marker -
+ * alternative to using end_markers
+ * FUNCTION
+ * This is an alternative to using end_markers - sometimes ROBODOC
+ * confuses asterisks commonly used in comments as an end marker. To
+ * use this footer instead of end_markers use the -rh switch.
+ * NOTE
+ * Added by David Druffner.
+ * SOURCE
+ */
+
+char *robo_end[] = { "/*ROBODOC_END*", "*ROBODOC_END*", NULL };
+
+/****/
+
+
+/***f* Headers/RB_Is_Begin_Marker
+ * FUNCTION
+ * Scan a line and see if any of the begin-of-a-header-markers
+ * defined in header_markers can be found.
+ * SYNOPSIS
+ */
+int RB_Is_Begin_Marker(
+ char *cur_line,
+ char **type )
+/*
+ * INPUTS
+ * cur_line -- line to be searched.
+ * OUTPUT
+ * type -- the kind of header
+ * RESULT
+ * TRUE -- a begin header was found
+ * FALSE -- no begin header was found.
+ * SOURCE
+ */
+{
+ int found = FALSE;
+ unsigned int marker = NO_MARKER; /* for the assert */
+ char *cur_mchar = NULL;
+ char *cur_char = NULL;
+
+ if ( !( course_of_action.do_robo_head )
+ &&
+ ( ( ( course_of_action.do_lockheader ) &&
+ ( locked_header_marker == NO_MARKER_LOCKED ) )
+ || !( course_of_action.do_lockheader ) ) )
+ {
+ for ( marker = 0;
+ ( marker < configuration.header_markers.number ) && !found;
+ marker++ )
+ {
+ cur_mchar = configuration.header_markers.names[marker];
+ for ( found = TRUE, cur_char = RB_Skip_Whitespace( cur_line );
+ *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
+ {
+ if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
+ found = FALSE;
+ }
+ if ( *cur_mchar != '\0' )
+ {
+ /* It is not a complete match */
+ found = FALSE;
+ }
+ }
+ }
+ else if ( ( course_of_action.do_lockheader ) &&
+ ( locked_header_marker != NO_MARKER_LOCKED ) )
+ {
+ cur_mchar = configuration.header_markers.names[locked_header_marker];
+ for ( found = TRUE, cur_char = RB_Skip_Whitespace( cur_line );
+ *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
+ {
+ if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
+ {
+ found = FALSE;
+ }
+ if ( *cur_mchar != '\0' )
+ {
+ /* It is not a complete match */
+ found = FALSE;
+ }
+ }
+ }
+ else
+ {
+ assert( 0 );
+ }
+
+
+ if ( found && cur_char )
+ {
+ *type = cur_char;
+ ++cur_char;
+ if ( *cur_char == '*' )
+ {
+ ++cur_char;
+ found = utf8_isspace( *cur_char );
+ }
+ else if ( *( cur_char + 1 ) == '*' )
+ {
+ ++cur_char;
+ ++cur_char;
+ found = utf8_isspace( *cur_char );
+ }
+ else
+ {
+ found = FALSE;
+ }
+ if ( found )
+ {
+ found = FALSE;
+ /* It should contain some non * characters. */
+ for ( ; *cur_char; ++cur_char )
+ {
+ if ( utf8_isalnum( *cur_char ) )
+ {
+ found = TRUE;
+ }
+ }
+ }
+ }
+
+ if ( found &&
+ ( course_of_action.do_lockheader ) &&
+ ( locked_header_marker == NO_MARKER_LOCKED ) )
+ {
+ assert( marker != NO_MARKER );
+ locked_header_marker = marker - 1;
+ RB_Say( "header marker locked on %s\n", SAY_INFO,
+ configuration.header_markers.names[locked_header_marker] );
+ }
+ return found;
+}
+
+/******/
+
+
+/* Generic function to skip a remark begin or end marker */
+
+static char *RB_Skip_Remark_XXX_Marker(
+ char *cur_line,
+ struct Parameters *parameters )
+{
+ char *cur_char;
+ char *space_pos;
+ unsigned int marker;
+ int found = FALSE;
+
+ cur_char = RB_Skip_Whitespace( cur_line );
+ space_pos = strchr( cur_char, ' ' );
+ /* Replace the first space on the line with a '\0'
+ * this makes the comparison with the remark markers
+ * much easier.
+ */
+ if ( space_pos )
+ {
+ *space_pos = '\0';
+ }
+
+ for ( marker = 0; ( marker < parameters->number ) && !found; marker++ )
+ {
+ found =
+ ( RB_Str_Case_Cmp( cur_char, parameters->names[marker] ) == 0 );
+ }
+
+ assert( found );
+
+ if ( space_pos )
+ {
+ *space_pos = ' ';
+ return space_pos;
+ }
+ else
+ {
+ return cur_char + strlen( parameters->names[marker - 1] );
+ }
+}
+
+
+/* Generic function to see if there is a remark begin or end marker */
+
+static int RB_Is_Remark_XXX_Marker(
+ char *cur_line,
+ struct Parameters *parameters )
+{
+ char *cur_char;
+ char *space_pos;
+ unsigned int marker;
+ int found = FALSE;
+
+ cur_char = RB_Skip_Whitespace( cur_line );
+ space_pos = strchr( cur_char, ' ' );
+ /* Replace the first space on the line with a '\0'
+ * this makes the comparison with the remark markers
+ * much easier.
+ */
+ if ( space_pos )
+ {
+ *space_pos = '\0';
+ }
+
+ for ( marker = 0; ( marker < parameters->number ) && !found; marker++ )
+ {
+ found =
+ ( RB_Str_Case_Cmp( cur_char, parameters->names[marker] ) == 0 );
+ }
+
+ if ( space_pos )
+ {
+ *space_pos = ' ';
+ }
+
+ return found;
+}
+
+
+/* TODO Documentation */
+int RB_Is_Remark_End_Marker(
+ char *cur_line )
+{
+ return RB_Is_Remark_XXX_Marker( cur_line,
+ &( configuration.remark_end_markers ) );
+}
+
+/* TODO Documentation */
+int RB_Is_Remark_Begin_Marker(
+ char *cur_line )
+{
+ return RB_Is_Remark_XXX_Marker( cur_line,
+ &( configuration.remark_begin_markers ) );
+}
+
+char *RB_Skip_Remark_Begin_Marker(
+ char *cur_line )
+{
+ return RB_Skip_Remark_XXX_Marker( cur_line,
+ &( configuration.
+ remark_begin_markers ) );
+}
+
+char *RB_Skip_Remark_End_Marker(
+ char *cur_line )
+{
+ return RB_Skip_Remark_XXX_Marker( cur_line,
+ &( configuration.remark_end_markers ) );
+}
+
+
+/****f* Headers/RB_Is_End_Marker
+ * FUNCTION
+ * Scan a line and see if any of the end of a header markers
+ * defined in header_markers can be found.
+ * SYNOPSIS
+ */
+int RB_Is_End_Marker(
+ char *cur_line )
+/*
+ * INPUTS
+ * cur_line -- line to be searched.
+ * OUTPUT
+ * none
+ * RESULT
+ * TRUE -- an end header was found
+ * FALSE -- none was found.
+ * SOURCE
+ */
+{
+ int found = FALSE;
+ unsigned int marker = NO_MARKER; /* For the assert */
+ char *cur_mchar;
+ char *cur_char;
+
+ if ( !( course_of_action.do_robo_head )
+ &&
+ ( ( ( course_of_action.do_lockheader ) &&
+ ( locked_end_marker == NO_MARKER_LOCKED ) )
+ || !( course_of_action.do_lockheader ) ) )
+ {
+ for ( marker = 0;
+ ( marker < configuration.end_markers.number ) && !found;
+ marker++ )
+ {
+ cur_mchar = configuration.end_markers.names[marker];
+ cur_char = RB_Skip_Whitespace( cur_line );
+ if ( *cur_char )
+ {
+ for ( found = TRUE;
+ *cur_mchar && *cur_char && found;
+ cur_mchar++, cur_char++ )
+ {
+ if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
+ {
+ found = FALSE;
+ }
+ }
+ }
+ }
+ }
+ else if ( ( course_of_action.do_lockheader ) &&
+ ( locked_end_marker != NO_MARKER_LOCKED ) )
+ {
+ cur_mchar = configuration.end_markers.names[locked_end_marker];
+ cur_char = RB_Skip_Whitespace( cur_line );
+ if ( *cur_char )
+ {
+ for ( found = TRUE;
+ *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
+ {
+ if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
+ {
+ found = FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ assert( 0 );
+ }
+
+ /* Locking on end markers does not work at the moment,
+ * because there can be more than one end marker for
+ * a given language. TODO
+ */
+#if 0
+ if ( found &&
+ ( course_of_action.do_LOCKHEADER ) &&
+ ( locked_end_marker == NO_MARKER_LOCKED ) )
+ {
+ assert( marker != NO_MARKER );
+ locked_end_marker = marker - 1;
+ RB_Say( "end marker locked on %s\n", SAY_INFO,
+ end_markers[locked_end_marker] );
+ }
+#endif
+ return found;
+}
+
+/*****/
+
+
+/****f* Headers/RB_Has_Remark_Marker
+ * FUNCTION
+ * Check if a line starts with a remark marker. This function
+ * assumes that the remark marker starts on the first character of
+ * the line.
+ * SYNOPSIS
+ */
+int RB_Has_Remark_Marker(
+ char *lline_buffer )
+/*
+ * INPUTS
+ * o lline_buffer -- the line of text.
+ * RESULT
+ * o TRUE -- it starts with a remark marker
+ * o FALSE -- it does not.
+ * SOURCE
+ */
+{
+ unsigned int marker = 0;
+ unsigned int marker_found = configuration.remark_markers.number;
+ int found = FALSE;
+ char *space_pos = NULL;
+
+ space_pos = strchr( lline_buffer, ' ' );
+
+ /* Replace the first space on the line with a '\0'
+ * this makes the comparison with the remark markers
+ * much easier.
+ */
+ if ( space_pos )
+ {
+ *space_pos = '\0';
+ }
+
+ if ( ( ( course_of_action.do_lockheader ) &&
+ ( locked_remark_marker == NO_MARKER_LOCKED ) )
+ || !( course_of_action.do_lockheader ) )
+ {
+ for ( marker = 0; marker < configuration.remark_markers.number;
+ marker++ )
+ {
+ if ( RB_Str_Case_Cmp
+ ( lline_buffer,
+ configuration.remark_markers.names[marker] ) == 0 )
+ {
+ marker_found = marker;
+ found = TRUE;
+ }
+ }
+ }
+ else
+ {
+ if ( RB_Str_Case_Cmp
+ ( lline_buffer,
+ configuration.remark_markers.names[locked_remark_marker] ) ==
+ 0 )
+ {
+ marker_found = marker;
+ found = TRUE;
+ }
+ }
+
+ if ( found &&
+ ( locked_remark_marker == NO_MARKER_LOCKED )
+ && ( course_of_action.do_lockheader ) )
+ {
+ assert( marker_found < configuration.remark_markers.number );
+ locked_remark_marker = marker_found;
+ RB_Say( "remark marker locked on %s\n", SAY_INFO,
+ configuration.remark_markers.names[locked_remark_marker] );
+ }
+
+ /* Restore the space we replaced with a '\0' */
+ if ( space_pos )
+ {
+ *space_pos = ' ';
+ }
+
+ return found;
+}
+
+/******/
+
+
+/****f* Headers/RB_Skip_Remark_Marker [2.0e]
+ * NAME
+ * RB_Skip_Remark_Marker
+ * SYNOPSIS
+ */
+char *RB_Skip_Remark_Marker(
+ char *lline_buffer )
+/*
+ * FUNCTION
+ * Scan and search for a recognized remark marker; skip past the
+ * marker to the body of the text
+ * SOURCE
+ */
+{
+ unsigned int marker, found;
+ char *cur_char, *cur_mchar;
+
+ found = FALSE;
+ cur_char = NULL;
+ for ( marker = 0;
+ ( marker < configuration.remark_markers.number ) && !found;
+ marker++ )
+ {
+ cur_mchar = configuration.remark_markers.names[marker];
+ for ( found = TRUE, cur_char = lline_buffer;
+ *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
+ {
+ if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
+ {
+ found = FALSE;
+ }
+ }
+ }
+ return ( cur_char );
+}
+
+/**************/
+
+
+
+
+/* TODO Documentation */
+void RB_Header_Lock_Reset(
+ void )
+{
+ locked_header_marker = NO_MARKER_LOCKED;
+ locked_end_marker = NO_MARKER_LOCKED;
+}
+
+void RB_Item_Lock_Reset(
+ void )
+{
+ locked_remark_marker = NO_MARKER_LOCKED;
+}
--- /dev/null
+#ifndef ROBODOC_HEADERS_H
+#define ROBODOC_HEADERS_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****s* Headers/RB_header
+ * FUNCTION
+ * This structure is used to store the headers that are extracted
+ * from the source files.
+ * MODIFICATION HISTORY
+ * * 8. August 1995: Koessi changed int version to char *version
+ * ATTRIBUTES
+ * * next -- used to store RB_headers in a RB_Part as
+ * a linked list.
+ * * parent -- the parent of this header
+ * * name -- the full name of the header
+ * modulename/functionname [versioninfo]
+ * * owner -- the Part that owns this header.
+ * * htype -- the type of this header.
+ * * items -- pointers to the items in this header.
+ * * version -- unused
+ * * function_name -- the functionname ( a better name would
+ * be objectname or thingy name,
+ * it is the name of the thing that is
+ * being documented ).
+ * * module_name -- the modulename
+ * * unique_name -- unique name used for labels.
+ * * file_name -- documentation file of this header.
+ * * lines -- content of the header
+ * * no_lines -- number of lines in the content.
+ * * line_number -- the line number at which the header was
+ * found in the source file.
+ * SOURCE
+ */
+
+struct RB_header
+{
+ struct RB_header *next;
+ struct RB_header *parent;
+ struct RB_Part *owner;
+ struct RB_HeaderType *htype;
+ struct RB_Item *items;
+ int is_internal;
+ char *name;
+ char **names;
+ int no_names;
+ char *version;
+ char *function_name;
+ char *module_name;
+ char *unique_name;
+ char *file_name;
+ char **lines;
+ int no_lines;
+ int line_number;
+};
+
+/*********/
+
+/****d* Headers/src_constants
+ * NAME
+ * src_constants -- numerals for header_markers
+ * NOTE
+ * Most of them seem to be unused at the moment.
+ * But it's better to keep it up to date for the
+ * eventuality of a later use by robohdrs.
+ * SOURCE
+ */
+
+#define SRC_C 0
+#define SRC_ACM 1 /* Added by David White for Aspen Custom Modeller */
+#define SRC_CPP 2 /* All values incremented 1 to allow for ACM */
+#define SRC_PASCAL 3
+#define SRC_PASCAL2 4
+#define SRC_APLUS 5 /* David White for Aspen Plus */
+#define SRC_ASM 6
+#define SRC_ASM2 7
+#define SRC_FORTRAN 8
+#define SRC_BASIC 9
+#define SRC_TEX 10
+#define SRC_SCRIPT 11
+#define SRC_COBOL 12
+#define SRC_OCCAM 13
+#define SRC_HTML 14
+#define SRC_HTML2 15
+#define SRC_GNUASM 16
+#define SRC_F902 17
+#define SRC_F90 18
+#define SRC_VB 20
+#define SRC_DBC 21
+
+/*********/
+
+/****d* Headers/src_remark_constants
+ * NAME
+ * src_remark_constants -- numerals for remark_markers
+ * NOTE
+ * Most of them seem to be unused at the moment.
+ * But it's better to keep it up to date for the
+ * eventuality of a later use by robohdrs.
+ * SOURCE
+ */
+
+#define SRC_R_C 0
+#define SRC_R_ACM 1 /* Added by David White for Aspen Custom Modeller */
+#define SRC_R_CPP 2 /* All values incremented 1 to allow for ACM */
+#define SRC_R_PASCAL 3 /* PASCAL and PASCAL2 were 1, should have been 2, incr to 3 */
+#define SRC_R_PASCAL2 3
+#define SRC_R_APLUS 4 /* David White for Aspen Plus */
+#define SRC_R_ASM 5
+#define SRC_R_ASM2 6
+#define SRC_R_FORTRAN 7
+#define SRC_R_BASIC 8
+#define SRC_R_TEX 9
+#define SRC_R_SCRIPT 10
+#define SRC_R_COBOL 11
+#define SRC_R_OCCAM 12
+#define SRC_R_GNUASM 13
+#define SRC_R_F902 14
+#define SRC_R_F90 15
+#define SRC_R_VB 17
+#define SRC_R_DBC 18
+#define SRC_R_HTML 19 /* NULL */
+#define SRC_R_HTML2 19 /* NULL */
+
+/*********/
+
+/****d* Headers/end_remark_constants [3.0h]
+ * NAME
+ * end_remark_constants -- numerals for end_markers
+ * NOTE
+ * Most of them seem to be unused at the moment. But it's better to
+ * keep it up to date for the eventuality of a later use by
+ * robohdrs.
+ * SOURCE
+ */
+
+#define SRC_E_C 0
+#define SRC_E_ACM 1 /* Added by David White for Aspen Custom Modeller */
+#define SRC_E_CPP 2 /* All values incremented 1 to allow for ACM */
+#define SRC_E_PASCAL 4
+#define SRC_E_PASCAL2 5
+#define SRC_E_APLUS 6 /* David White for Aspen Plus */
+#define SRC_E_ASM 7
+#define SRC_E_ASM2 8
+#define SRC_E_FORTRAN 9
+#define SRC_E_BASIC 10
+#define SRC_E_TEX 11
+#define SRC_E_SCRIPT 12
+#define SRC_E_COBOL 13
+#define SRC_E_OCCAM 14
+#define SRC_E_HTML 15
+#define SRC_E_HTML2 16
+#define SRC_E_GNUASM 17
+#define SRC_E_F902 18
+#define SRC_E_F90 19
+#define SRC_E_VB 21
+#define SRC_E_DBC 22
+
+
+/*********/
+
+extern char *robo_header; /* Added by DavidCD */
+extern char *robo_end[]; /* Added by DavidCD */
+extern char *header_markers[];
+extern char *remark_markers[];
+extern char *end_markers[];
+extern char *end_remark_markers[];
+extern char *RB_header_type_names[];
+extern char *RB_internal_header_type_names[];
+
+int RB_Is_Begin_Marker(
+ char *cur_line,
+ char **type );
+int RB_Is_End_Marker(
+ char *cur_line );
+void RB_Header_Lock_Reset(
+ void );
+void RB_Item_Lock_Reset(
+ void );
+char *RB_Skip_Remark_Marker(
+ char *lline_buffer );
+int RB_Has_Remark_Marker(
+ char *lline_buffer );
+
+int RB_Is_Remark_End_Marker(
+ char *cur_line );
+int RB_Is_Remark_Begin_Marker(
+ char *cur_line );
+char *RB_Skip_Remark_Begin_Marker(
+ char *cur_line );
+char *RB_Skip_Remark_End_Marker(
+ char *cur_line );
+
+#endif /* ROBODOC_HEADERS_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+
+/****h* ROBODoc/HeaderTypes
+ * FUNCTION
+ * Headers come in different types. This module defines what kind
+ * of headertypes ROBODoc recognizes, and contains functions to add
+ * new headertypes and to compare headertypes. All the headertypes
+ * are stored in an array, header_type_lookup_table.
+ ******
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "headertypes.h"
+#include "util.h"
+
+/****v* HeaderTypes/header_type_lookup_table
+ * FUNCTION
+ * A lookup table for all the header types that ROBODoc recognizes.
+ * At the moment is has about 127 entries. About as many as there
+ * are characters in the standard ASCII set. The first 32 entries
+ * can be used for special purposes.
+ *
+ * Two of them are use:
+ * HT_MASTERINDEXTYPE
+ * and
+ * HT_SOURCEHEADERTYPE
+ *
+ * HT_MASTERINDEXTYPE is a wildcard type. All headertypes match this
+ * type. This is used to collect all the headers for the
+ * masterindex.
+ *
+ * HT_SOURCEHEADERTYPE is used to pretend that the name of
+ * a sourcefile is a kind of header. This makes it possible to
+ * include the names of the source files in the master index.
+ * SOURCE
+ */
+
+struct RB_HeaderType header_type_lookup_table[MAX_HEADER_TYPE + 1] = {
+ {'\0', NULL, NULL, 0},
+ {HT_SOURCEHEADERTYPE, "Sourcefiles", "robo_sourcefiles", 0},
+ {HT_MASTERINDEXTYPE, "Index", "masterindex", 0}, /* no robo_ prefix for backwards compatibility */
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {' ', NULL, NULL, 0},
+ {'!', NULL, NULL, 0},
+ {'"', NULL, NULL, 0},
+ {'#', NULL, NULL, 0},
+ {'$', NULL, NULL, 0},
+ {'%', NULL, NULL, 0},
+ {'&', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0},
+ {'(', NULL, NULL, 0},
+ {')', NULL, NULL, 0},
+ {'*', "Generics", "robo_generics", 0},
+ {'+', NULL, NULL, 0},
+ {',', NULL, NULL, 0},
+ {'-', NULL, NULL, 0},
+ {'.', NULL, NULL, 0},
+ {'/', NULL, NULL, 0},
+ {'0', NULL, NULL, 0},
+ {'1', NULL, NULL, 0},
+ {'2', NULL, NULL, 0},
+ {'3', NULL, NULL, 0},
+ {'4', NULL, NULL, 0},
+ {'5', NULL, NULL, 0},
+ {'6', NULL, NULL, 0},
+ {'7', NULL, NULL, 0},
+ {'8', NULL, NULL, 0},
+ {'9', NULL, NULL, 0},
+ {':', NULL, NULL, 0},
+ {';', NULL, NULL, 0},
+ {'<', NULL, NULL, 0},
+ {'=', NULL, NULL, 0},
+ {'>', NULL, NULL, 0},
+ {'?', NULL, NULL, 0},
+ {'@', NULL, NULL, 0},
+ {'A', NULL, NULL, 0},
+ {'B', "Businessrules", "robo_businessrules", 0},
+ {'C', "Contracts", "robo_contracts", 0},
+ {'D', "Datasources", "robo_datasources", 0},
+ {'E', "Ensure contracts", "robo_ensure_contracts", 0},
+ {'F', NULL, NULL, 0},
+ {'G', NULL, NULL, 0},
+ {'H', NULL, NULL, 0},
+ {'I', "Invariants", "robo_invariants", 0},
+ {'J', NULL, NULL, 0},
+ {'K', NULL, NULL, 0},
+ {'L', NULL, NULL, 0},
+ {'M', "Metadata", "robo_metadata", 0},
+ {'N', NULL, NULL, 0},
+ {'O', NULL, NULL, 0},
+ {'P', "Process", "robo_processes", 0},
+ {'Q', NULL, NULL, 0},
+ {'R', "Require contracts", "robo_require_contracts", 0},
+ {'S', "Subjects", "robo_subjects", 0},
+ {'T', NULL, NULL, 0},
+ {'U', NULL, NULL, 0},
+ {'V', NULL, NULL, 0},
+ {'W', NULL, NULL, 0},
+ {'X', NULL, NULL, 0},
+ {'Y', NULL, NULL, 0},
+ {'Z', NULL, NULL, 0},
+ {'[', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0}, /* Separator / */
+ {']', NULL, NULL, 0},
+ {'^', NULL, NULL, 0},
+ {'_', NULL, NULL, 0},
+ {'`', NULL, NULL, 0},
+ {'a', NULL, NULL, 0},
+ {'b', NULL, NULL, 0},
+ {'c', "Classes", "robo_classes", 0},
+ {'d', "Definitions", "robo_definitions", 0},
+ {'e', "Exceptions", "robo_exceptions", 0},
+ {'f', "Functions", "robo_functions", 0},
+ {'g', NULL, NULL, 0},
+ {'h', "Modules", "robo_modules", 1},
+ {'\0', NULL, NULL, 0}, /* Internal header flag */
+ {'j', NULL, NULL, 0},
+ {'k', NULL, NULL, 0},
+ {'l', NULL, NULL, 0},
+ {'m', "Methods", "robo_methods", 0},
+ {'n', NULL, NULL, 0},
+ {'o', NULL, NULL, 0},
+ {'p', "Procedures", "robo_procedures", 0},
+ {'q', NULL, NULL, 0},
+ {'r', NULL, NULL, 0},
+ {'s', "Structures", "robo_strutures", 0},
+ {'t', "Types", "robo_types", 0},
+ {'u', "Unittest", "robo_unittests", 0},
+ {'v', "Variables", "robo_variables", 0},
+ {'w', "Warehouses", "robo_warehouses", 0},
+ {'x', NULL, NULL, 0},
+ {'y', NULL, NULL, 0},
+ {'z', NULL, NULL, 0},
+ {'{', NULL, NULL, 0},
+ {'|', NULL, NULL, 0},
+ {'}', NULL, NULL, 0},
+ {'~', NULL, NULL, 0},
+ {'\0', NULL, NULL, 0}
+};
+
+/*****/
+
+
+/****f* HeaderTypes/RB_AddHeaderType
+ * FUNCTION
+ * Add a new headertype to the list of header type
+ * that robodoc recognizes.
+ * RESULT
+ * * FALSE -- it is a new header type.
+ * * TRUE -- header type already existed.
+ * SOURCE
+ */
+
+int RB_AddHeaderType(
+ unsigned int typeCharacter,
+ char *indexName,
+ char *fileName,
+ unsigned int priority )
+{
+ if ( header_type_lookup_table[typeCharacter].typeCharacter )
+ {
+ header_type_lookup_table[typeCharacter].indexName = indexName;
+ header_type_lookup_table[typeCharacter].fileName = fileName;
+ header_type_lookup_table[typeCharacter].priority = priority;
+ }
+ else
+ {
+ RB_Panic
+ ( "The character %c is not allowed as a headertype character\n",
+ typeCharacter );
+ }
+ /* Unused */
+ return 0;
+}
+
+/*****/
+
+
+
+/****f* HeaderTypes/RB_CompareHeaderTypes
+ * FUNCTION
+ * Compare two header types and check if they are equal. If one of
+ * the header types is a HT_MASTERINDEXTYPE the comparison is always
+ * TRUE. (This to make sure that all headers appear in the Index.)
+ * SYNOPSIS
+ * int RB_CompareHeaderTypes(
+ * struct RB_HeaderType* ht1, struct RB_HeaderType* ht2 )
+ * INPUTS
+ * o ht1 and ht2 -- the header types to compare.
+ * RESULT
+ * o 0 -- header types are not equal
+ * o != 0 -- header type are equal
+ * SOURCE
+ */
+
+int RB_CompareHeaderTypes(
+ struct RB_HeaderType *ht1,
+ struct RB_HeaderType *ht2 )
+{
+ assert( ht1 );
+ assert( ht2 );
+ return ( ht1->typeCharacter == HT_MASTERINDEXTYPE ) ||
+ ( ht2->typeCharacter == HT_MASTERINDEXTYPE ) ||
+ ( ht1->typeCharacter == ht2->typeCharacter );
+}
+
+/******/
+
+
+
+/****f* HeaderTypes/RB_FindHeaderType
+ * FUNCTION
+ * Return the header type that corresponds to the type character.
+ * RESULT
+ * * 0 -- there is no such header type
+ * * pointer to the header type otherwise.
+ * SOURCE
+ */
+
+struct RB_HeaderType *RB_FindHeaderType(
+ unsigned char typeCharacter )
+{
+ struct RB_HeaderType *headertype = 0;
+
+ if ( typeCharacter < MAX_HEADER_TYPE )
+ {
+ headertype = &( header_type_lookup_table[typeCharacter] );
+ if ( ( headertype->typeCharacter == typeCharacter ) &&
+ ( headertype->indexName ) )
+ {
+ return headertype;
+ }
+ }
+ return 0;
+}
+
+/*****/
+
+
+/****f* HeaderTypes/RB_IsInternalHeader
+ * FUNCTION
+ * Given the typeCharacter is this an internal header?
+ * RESULT
+ * * TRUE -- yes it is
+ * * FALSE -- no it is not
+ * SOURCE
+ */
+
+int RB_IsInternalHeader(
+ unsigned char type_character )
+{
+ return ( type_character == 'i' );
+}
+
+/*****/
+
+
+
+#if 0
+char *RB_GetIndexName(
+ struct RB_HeaderType *ht1 )
+{
+ /* TODO should be used to access indexName */
+ return 0;
+}
+#endif
--- /dev/null
+#ifndef ROBODOC_HEADERTYPES_H
+#define ROBODOC_HEADERTYPES_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****s* Headers/RB_HeaderType
+ * NAME
+ * RB_HeaderType -- Information about a header type
+ * ATTRIBUTES
+ * o typeCharacter -- The character used to indicate it
+ * o indexName -- The name used for the master index
+ * o fileName -- The name of the file use to store
+ * the master index for this type of headers.
+ * o priority -- The sorting priority of this header.
+ * Higher priorities appear first
+ * SOURCE
+ */
+
+struct RB_HeaderType
+{
+ unsigned char typeCharacter;
+ char *indexName;
+ char *fileName;
+ unsigned int priority;
+};
+
+/*******/
+
+#define HT_SOURCEHEADERTYPE ((unsigned char)1)
+#define HT_MASTERINDEXTYPE ((unsigned char)2)
+
+#define MIN_HEADER_TYPE 1 /* ' ' */
+#define MAX_HEADER_TYPE 127
+
+
+int RB_AddHeaderType(
+ unsigned int typeCharacter,
+ char *indexName,
+ char *indexFile,
+ unsigned int priority );
+struct RB_HeaderType *RB_FindHeaderType(
+ unsigned char typeCharacter );
+void RB_InitHeaderTypes(
+ void );
+int RB_IsInternalHeader(
+ unsigned char type_character );
+int RB_CompareHeaderTypes(
+ struct RB_HeaderType *ht1,
+ struct RB_HeaderType *ht2 );
+
+#endif /* ROBODOC_HEADERTYPES_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/HTML_Generator
+ * FUNCTION
+ * The generator for HTML output.
+ *
+ * The generator supports sections upto 7 levels deep. It supports
+ * a Table of Contents based on all headers. A masterindex for
+ * all headertypes and seperate masterindexes for each headertype.
+ *
+ * MODIFICATION HISTORY
+ * 2003-02-03 Frans Slothouber Refactoring
+ * ????-??-?? Frans Slothouber V1.0
+ *******
+ * $Id: html_generator.c,v 1.91 2007/07/10 19:13:51 gumpu Exp $
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "html_generator.h"
+#include "util.h"
+#include "globals.h"
+#include "robodoc.h"
+#include "links.h"
+#include "headers.h"
+#include "headertypes.h"
+#include "generator.h"
+#include "items.h"
+#include "string.h"
+#include "document.h"
+#include "directory.h"
+#include "path.h"
+#include "part.h"
+#include "roboconfig.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+static char *css_name = NULL;
+static int in_linecomment = 0; // are we in a line comment?
+
+static void RB_HTML_Generate_String(
+ FILE *dest_doc,
+ const char *a_string );
+
+
+
+/* TODO Documentation */
+static void HTML_Generate_Div(
+ FILE *dest_doc,
+ char *id )
+{
+
+ fprintf( dest_doc, "<div id=\"%s\">\n", id );
+}
+
+
+/* TODO Documentation */
+static void HTML_Generate_Div_End(
+ FILE *dest_doc,
+ char *id )
+{
+ fprintf( dest_doc, "</div> <!-- %s -->\n", id );
+}
+
+
+/* TODO Documentation */
+
+char *HTML_TOC_Index_Filename(
+ struct RB_Document *document )
+{
+ char *toc_index_path = NULL;
+ char *toc_index_name = "toc_index.html";
+
+ assert( document->docroot->name );
+
+ toc_index_path =
+ calloc( strlen( toc_index_name ) + 2 +
+ strlen( document->docroot->name ), 1 );
+ strcpy( toc_index_path, document->docroot->name );
+ strcat( toc_index_path, toc_index_name );
+
+ return toc_index_path;
+}
+
+
+
+/* TODO Documentation */
+static void RB_HTML_Generate_Source_Tree_Entry(
+ FILE *dest_doc,
+ char *dest_name,
+ struct RB_Path *parent_path,
+ struct RB_Directory *srctree,
+ struct RB_Document *document )
+{
+ struct RB_Path *cur_path;
+ struct RB_Filename *cur_filename;
+
+ fprintf( dest_doc, "<ul>\n" );
+
+ for ( cur_filename = srctree->first;
+ cur_filename; cur_filename = cur_filename->next )
+ {
+ if ( cur_filename->path == parent_path )
+ {
+ char *r = 0;
+
+ if ( cur_filename->link )
+ {
+ if ( document->actions.do_one_file_per_header )
+ {
+ fprintf( dest_doc, "<li><tt>\n" );
+ RB_HTML_Generate_String( dest_doc, cur_filename->name );
+ fprintf( dest_doc, "</tt></li>\n" );
+ }
+ else
+ {
+ r = RB_HTML_RelativeAddress( dest_name,
+ cur_filename->link->
+ file_name );
+ fprintf( dest_doc, "<li>\n" );
+ fprintf( dest_doc, "<a href=\"%s#%s\"><tt>\n", r,
+ cur_filename->link->label_name );
+ RB_HTML_Generate_String( dest_doc, cur_filename->name );
+ fprintf( dest_doc, "</tt></a></li>\n" );
+ }
+ }
+ }
+ }
+ for ( cur_path = srctree->first_path;
+ cur_path; cur_path = cur_path->next )
+ {
+ if ( cur_path->parent == parent_path )
+ {
+ fprintf( dest_doc, "<li>\n" );
+ RB_HTML_Generate_String( dest_doc, cur_path->name );
+ RB_HTML_Generate_Source_Tree_Entry( dest_doc, dest_name, cur_path,
+ srctree, document );
+ fprintf( dest_doc, "</li>\n" );
+ }
+ }
+ fprintf( dest_doc, "</ul>\n" );
+}
+
+/* TODO Documentation */
+void RB_HTML_Generate_Source_Tree(
+ FILE *dest_doc,
+ char *dest_name,
+ struct RB_Document *document )
+{
+ struct RB_Directory *srctree;
+
+ srctree = document->srctree;
+ RB_HTML_Generate_Source_Tree_Entry( dest_doc, dest_name, NULL, srctree,
+ document );
+}
+
+
+/****if* HTML_Generator/RB_HTML_Generate_String
+ * FUNCTION
+ * Write a string to the destination document, escaping
+ * characters where necessary.
+ * SYNOPSIS
+ */
+static void RB_HTML_Generate_String(
+ FILE *dest_doc,
+ const char *a_string )
+/*
+ * INPUTS
+ * o dest_doc -- the file the characters are written too
+ * o a_string -- a nul terminated string.
+ * SEE ALSO
+ * RB_HTML_Generate_Char()
+ * SOURCE
+ */
+{
+ int i;
+ int l = strlen( a_string );
+ unsigned char c;
+
+ for ( i = 0; i < l; ++i )
+ {
+ c = a_string[i];
+ RB_HTML_Generate_Char( dest_doc, c );
+ }
+}
+
+/*******/
+
+
+/****if* HTML_Generator/RB_HTML_Generate_False_Link
+ * FUNCTION
+ * Create a representation for a link that links an word in
+ * a header to the header itself.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+/*
+ * INPUTS
+ * * dest_doc -- the file the representation is written to.
+ * * name -- the word.
+ * SOURCE
+ */
+{
+ fprintf( dest_doc, "<strong>" );
+ RB_HTML_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "</strong>" );
+
+}
+
+/*******/
+
+
+/****f* HTML_Generator/RB_HTML_Color_String
+ * FUNCTION
+ * Generates various colored strings
+ * SOURCE
+ */
+
+static void RB_HTML_Color_String(
+ FILE *dest_doc,
+ int open,
+ const char *class,
+ const char *string )
+{
+ switch ( open )
+ {
+ // string, closing
+ case 0:
+ RB_HTML_Generate_String( dest_doc, string );
+ fprintf( dest_doc, "</span>" );
+ break;
+
+ // opening, string
+ case 1:
+ fprintf( dest_doc, "<span class=\"%s\">", class );
+ RB_HTML_Generate_String( dest_doc, string );
+ break;
+
+ // opening, string, closing
+ case 2:
+ fprintf( dest_doc, "<span class=\"%s\">", class );
+ RB_HTML_Generate_String( dest_doc, string );
+ fprintf( dest_doc, "</span>" );
+ break;
+
+ // opening, char, closing
+ case 3:
+ fprintf( dest_doc, "<span class=\"%s\">", class );
+ RB_HTML_Generate_Char( dest_doc, *string );
+ fprintf( dest_doc, "</span>" );
+ break;
+
+ // Bug
+ default:
+ assert( 0 );
+ }
+}
+
+/*******/
+
+
+/****f* HTML_Generator/RB_HTML_Generate_Line_Comment_End
+ * FUNCTION
+ * Check if a line comment is active and generate ending sequence for it.
+ * Should be called at the end of each SOURCE line.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Line_Comment_End(
+ FILE *dest_doc )
+{
+ // Check if we are in a line comment
+ if ( in_linecomment )
+ {
+ // and end the line comment
+ in_linecomment = 0;
+ RB_HTML_Color_String( dest_doc, in_linecomment, COMMENT_CLASS, "" );
+ }
+}
+
+/*******/
+
+
+/****f* HTML_Generator/RB_HTML_Generate_Extra
+ * FUNCTION
+ * Do some additional processing to detect HTML extra's like
+ * file references and other kind of links for the documentation
+ * body of an item.
+ * SYNOPSIS
+ */
+int RB_HTML_Generate_Extra(
+ FILE *dest_doc,
+ enum ItemType item_type,
+ char *cur_char,
+ char prev_char )
+/*
+ * INPUTS
+ * o dest_doc -- the file to write to.
+ * o item_type -- the kind of item the body belongs to.
+ * o cur_char -- pointer to a substring of the item's body
+ * o prev_char -- the character just before cur char (zero if none)
+ * RESULTS
+ * Number of characters produced.
+ * SOURCE
+ */
+{
+ char link[1024], *str;
+ int res = -1;
+ unsigned int i;
+ static int incomment = 0; /* are we in comment? */
+ static int quote = 0; /* double quote */
+ static int squote = 0; /* single quote */
+
+ // Reset comment and quote state machine if not source item
+ if ( !Works_Like_SourceItem( item_type ) )
+ {
+ quote = 0;
+ squote = 0;
+ incomment = 0;
+ in_linecomment = 0;
+ }
+ // else check for quotations and string literals
+ else if ( !( incomment || in_linecomment ) )
+ {
+ switch ( *cur_char )
+ {
+ // Check for quoted string literals ("string")
+ case '\"':
+ if ( !squote && course_of_action.do_quotes )
+ {
+ if ( prev_char != '\\' )
+ {
+ quote = !quote;
+ RB_HTML_Color_String( dest_doc, quote,
+ QUOTE_CLASS, "\"" );
+ return 0;
+ }
+ else if ( quote && *( ( char * ) ( cur_char - 2 ) ) == '\\' )
+ {
+ quote = !quote; /* case "... \\" */
+ RB_HTML_Color_String( dest_doc, quote,
+ QUOTE_CLASS, "\"" );
+ return 0;
+ }
+ }
+ break;
+
+ // Check for single quoted string literals ('string')
+ case '\'':
+ if ( !quote && course_of_action.do_squotes )
+ {
+ if ( prev_char != '\\' )
+ {
+ squote = !squote;
+ RB_HTML_Color_String( dest_doc, squote,
+ SQUOTE_CLASS, "\'" );
+ return 0;
+ }
+ else if ( squote && *( ( char * ) ( cur_char - 2 ) ) == '\\' )
+ {
+ squote = !squote; /* case '\\' */
+ RB_HTML_Color_String( dest_doc, squote,
+ SQUOTE_CLASS, "\'" );
+ return 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Recognise line comments
+ if ( Works_Like_SourceItem( item_type ) && !incomment && !quote
+ && !squote && course_of_action.do_line_comments )
+ {
+ // check for line comment start
+ if ( !in_linecomment )
+ {
+ str =
+ Find_Parameter_Partial( &
+ ( configuration.
+ source_line_comments ), cur_char );
+ if ( str )
+ {
+ in_linecomment = 1;
+ RB_HTML_Color_String( dest_doc, in_linecomment,
+ COMMENT_CLASS, str );
+ // We found it, so exit
+ return strlen( str ) - 1;
+ }
+ }
+ // The end of line comments are generated in
+ // RB_HTML_Generate_Line_Comment_End()
+ }
+
+ // Recognise block comments
+ if ( Works_Like_SourceItem( item_type ) && !in_linecomment && !quote
+ && !squote && course_of_action.do_block_comments )
+ {
+ // Check for block comment start
+ if ( !incomment )
+ {
+ str =
+ Find_Parameter_Partial( &
+ ( configuration.
+ remark_begin_markers ), cur_char );
+ if ( str )
+ {
+ incomment = 1;
+ RB_HTML_Color_String( dest_doc, incomment,
+ COMMENT_CLASS, str );
+ // We found it, so exit
+ return strlen( str ) - 1;
+ }
+ }
+ // Check for block comment end
+ else
+ {
+ str =
+ Find_Parameter_Partial( &( configuration.remark_end_markers ),
+ cur_char );
+ if ( str )
+ {
+ incomment = 0;
+ RB_HTML_Color_String( dest_doc, incomment,
+ COMMENT_CLASS, str );
+ // We found it, so exit
+ return strlen( str ) - 1;
+ }
+ }
+ }
+
+ // Do further source formating
+ if ( Works_Like_SourceItem( item_type ) &&
+ !in_linecomment && !incomment && !quote && !squote )
+ {
+ // Check for keywords
+ if ( configuration.keywords.number && course_of_action.do_keywords )
+ {
+ char *keyword;
+
+ // Check if we are at the beginning of a word
+ if ( !utf8_isalnum( prev_char ) && ( prev_char != '_' ) )
+ {
+ // Count word length
+ for ( i = 1; // A word should have at least one character...
+ utf8_isalnum( cur_char[i] ) || ( cur_char[i] == '_' );
+ i++ );
+ // Check if it is a keyword
+ if ( ( keyword = Find_Keyword( cur_char, i ) ) )
+ {
+ RB_HTML_Color_String( dest_doc, 2, KEYWORD_CLASS,
+ keyword );
+ // Exit function
+ return i - 1;
+ }
+ }
+ }
+
+ // Do some fancy coloration for non-alphanumeric chars
+ if ( !utf8_isalnum( *cur_char ) && *cur_char != '_'
+ && *cur_char != ' ' && course_of_action.do_non_alpha )
+ {
+ RB_HTML_Color_String( dest_doc, 3, SIGN_CLASS, cur_char );
+ return 0;
+ }
+ }
+
+ // Check for links, etc...
+ if ( incomment || in_linecomment || !Works_Like_SourceItem( item_type ) )
+ {
+ if ( strncmp( "http://", cur_char, 7 ) == 0 )
+ {
+ sscanf( cur_char, "%s", link );
+ RB_Say( "found link %s\n", SAY_DEBUG, link );
+ res = ( strlen( link ) - 1 );
+ /* [ 697247 ] http://body. does not skip the '.' */
+ if ( link[( strlen( link ) - 1 )] == '.' )
+ {
+ link[( strlen( link ) - 1 )] = '\0';
+ fprintf( dest_doc, "<a href=\"%s\">%s</a>.", link, link );
+ }
+ else
+ {
+ fprintf( dest_doc, "<a href=\"%s\">%s</a>", link, link );
+ }
+ }
+ else if ( strncmp( "href:", cur_char, 5 ) == 0 )
+ {
+ /*
+ * handy in relative hyperlink paths, e.g.
+ * href:../../modulex/
+ */
+ sscanf( ( cur_char + 5 ), "%s", link );
+ RB_Say( "found link %s\n", SAY_DEBUG, link );
+ res = ( strlen( link ) + 4 );
+ fprintf( dest_doc, "<a href=\"%s\">%s</a>", link, link );
+ }
+ else if ( strncmp( "file:/", cur_char, strlen( "file:/" ) ) == 0 )
+ {
+ sscanf( cur_char, "%s", link );
+ RB_Say( "found link %s\n", SAY_DEBUG, link );
+ res = ( strlen( link ) - 1 );
+ fprintf( dest_doc, "<a href=\"%s\">%s</a>", link, link );
+ }
+ else if ( strncmp( "mailto:", cur_char, 7 ) == 0 )
+ {
+ sscanf( ( cur_char + 7 ), "%s", link );
+ RB_Say( "found mail to %s\n", SAY_DEBUG, link );
+ res = ( strlen( link ) + 6 );
+ fprintf( dest_doc, "<a href=\"mailto:%s\">%s</a>", link, link );
+ }
+ else if ( strncmp( "image:", cur_char, 6 ) == 0 )
+ {
+ sscanf( ( cur_char + 6 ), "%s", link );
+ RB_Say( "found image %s\n", SAY_DEBUG, link );
+ res = ( strlen( link ) + 5 );
+ fprintf( dest_doc, "<img src=\"%s\">", link );
+ }
+
+ }
+ return res;
+}
+
+/******/
+
+
+
+void RB_HTML_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "<p class=\"item_name\">" );
+ RB_HTML_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "</p>\n" );
+}
+
+void RB_HTML_Generate_Item_Begin(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ /* empty */
+}
+
+void RB_HTML_Generate_Item_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ /* empty */
+}
+
+
+int sectiontoc_counters[MAX_SECTION_DEPTH];
+
+/****f* HTML_Generator/RB_HTML_Generate_TOC_Section
+ * FUNCTION
+ * Create a table of contents based on the hierarchy of
+ * the headers starting for a particular point in this
+ * hierarchy (the parent).
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_TOC_Section(
+ FILE *dest_doc,
+ char *dest_name,
+ struct RB_header *parent,
+ struct RB_header **headers,
+ int count,
+ int depth )
+/*
+ * INPUTS
+ * o dest_doc -- the file to write to.
+ * o dest_name -- the name of this file.
+ * o parent -- the parent of the headers for which the the
+ * current level(depth) of TOC is created.
+ * o headers -- an array of headers for which the TOC is created
+ * o count -- the number of headers in this array
+ * o depth -- the current depth of the TOC
+ * NOTES
+ * This is a recursive function and tricky stuff.
+ * SOURCE
+ */
+{
+ struct RB_header *header;
+ int i, n, once = 0;
+
+ ++sectiontoc_counters[depth];
+
+ for ( i = depth + 1; i < MAX_SECTION_DEPTH; ++i )
+ {
+ sectiontoc_counters[i] = 0;
+ }
+
+ // List item start
+ fprintf( dest_doc, "<li>" );
+
+ // Do not generate section numbers if sectionnameonly
+ if ( !( course_of_action.do_sectionnameonly ) )
+ {
+ for ( i = 1; i <= depth; ++i )
+ {
+ fprintf( dest_doc, "%d.", sectiontoc_counters[i] );
+ }
+ fprintf( dest_doc, " " );
+ }
+
+
+ // Generate Link to first reference name
+ RB_HTML_Generate_Link( dest_doc, dest_name, parent->file_name,
+ parent->unique_name,
+ // only generate function name if sectionnameonly
+ ( course_of_action.do_sectionnameonly ) ?
+ parent->function_name : parent->name, 0 );
+
+ // Generate links to further reference names
+ for ( n = 1; n < parent->no_names; n++ )
+ {
+ RB_HTML_Generate_String( dest_doc, ", " );
+ RB_HTML_Generate_Link( dest_doc, dest_name, parent->file_name,
+ parent->unique_name, parent->names[n], 0 );
+ }
+
+ // List item end
+ fprintf( dest_doc, "</li>\n" );
+
+ for ( i = 0; i < count; ++i )
+ {
+ header = headers[i];
+ if ( header->parent == parent )
+ {
+ // Generate better TOC level hiearchy (Thuffir)
+ // We only generate <ul> once for a level
+ if ( !once )
+ {
+ once = 1;
+ fprintf( dest_doc, "<ul>\n" );
+ }
+ RB_HTML_Generate_TOC_Section( dest_doc, dest_name, header,
+ headers, count, depth + 1 );
+ }
+ else
+ {
+ /* Empty */
+ }
+ }
+ // If we have generated an <ul> before, generate the closing one too.
+ if ( once )
+ fprintf( dest_doc, "</ul>\n" );
+}
+
+/*******/
+
+
+void RB_HTML_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count,
+ struct RB_Part *owner,
+ char *dest_name )
+{
+ struct RB_header *header;
+ int i, j;
+ int depth = 1;
+
+ for ( i = 0; i < MAX_SECTION_DEPTH; ++i )
+ {
+ sectiontoc_counters[i] = 0;
+ }
+ fprintf( dest_doc, "<h3>TABLE OF CONTENTS</h3>\n" );
+ if ( course_of_action.do_sections )
+ {
+ /* --sections was specified, create a TOC based on the
+ * hierarchy of the headers.
+ */
+ fprintf( dest_doc, "<ul>\n" );
+ for ( i = 0; i < count; ++i )
+ {
+ header = headers[i];
+ if ( owner == NULL )
+ {
+ if ( header->parent )
+ {
+ /* Will be done in the subfunction */
+ }
+ else
+ {
+ RB_HTML_Generate_TOC_Section( dest_doc, dest_name, header,
+ headers, count, depth );
+ }
+ }
+ else
+ {
+ /* This is the TOC for a specific RB_Part (MultiDoc
+ * documentation). We only include the headers that
+ * are part of the subtree. That is, headers that are
+ * parth the RB_Part, or that are childern of the
+ * headers in the RB_Part.
+ */
+ if ( header->owner == owner )
+ {
+ /* BUG 721690 */
+ /* Any of the parents of this header should not
+ * have the same owner as this header, otherwise
+ * this header will be part of the TOC multiple times.
+ */
+ int no_bad_parent = TRUE;
+ struct RB_header *parent = header->parent;
+
+ for ( ; parent; parent = parent->parent )
+ {
+ if ( parent->owner == owner )
+ {
+ no_bad_parent = FALSE;
+ break;
+ }
+ }
+ if ( no_bad_parent )
+ {
+ RB_HTML_Generate_TOC_Section( dest_doc, dest_name,
+ header, headers, count,
+ depth );
+ }
+ }
+ }
+ }
+ fprintf( dest_doc, "</ul>\n" );
+ }
+ else
+ {
+ /* No --section option, generate a plain, one-level
+ * TOC
+ */
+ fprintf( dest_doc, "<ul>\n" );
+
+ for ( i = 0; i < count; ++i )
+ {
+ header = headers[i];
+ if ( header->name && header->function_name &&
+ ( ( owner == NULL ) || ( header->owner == owner ) ) )
+ {
+ for ( j = 0; j < header->no_names; j++ )
+ {
+ fprintf( dest_doc, "<li>" );
+
+ RB_HTML_Generate_Link( dest_doc, dest_name,
+ header->file_name,
+ header->unique_name,
+ header->names[j], 0 );
+ fprintf( dest_doc, "</li>\n" );
+ }
+ }
+ }
+ fprintf( dest_doc, "</ul>\n" );
+ }
+}
+
+
+
+/****f* HTML_Generator/RB_HTML_Generate_Label
+ * FUNCTION
+ * Generate a label (name) that can be refered too.
+ * A label should consist of only alphanumeric characters so
+ * all 'odd' characters are replaced with their ASCII code in
+ * hex format.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Label(
+ FILE *dest_doc,
+ char *name )
+/*
+ * INPUTS
+ * o dest_doc -- the file to write it to.
+ * o name -- the name of the label.
+ * SOURCE
+ */
+{
+ int i;
+ int l = strlen( name );
+ unsigned char c;
+
+ fprintf( dest_doc, "<a name=\"" );
+ for ( i = 0; i < l; ++i )
+ {
+ c = name[i];
+ if ( utf8_isalnum( c ) )
+ {
+ RB_HTML_Generate_Char( dest_doc, c );
+ }
+ else
+ {
+ char buf[4];
+
+ sprintf( buf, "%02x", c );
+ RB_HTML_Generate_Char( dest_doc, buf[0] );
+ RB_HTML_Generate_Char( dest_doc, buf[1] );
+ }
+ }
+ fprintf( dest_doc, "\">\n" );
+}
+
+/********/
+
+
+
+static int section_counters[MAX_SECTION_DEPTH];
+
+
+/* TODO Documentation */
+
+void RB_HTML_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header )
+{
+ int i;
+
+ ++section_counters[depth];
+ for ( i = depth + 1; i < MAX_SECTION_DEPTH; ++i )
+ {
+ section_counters[i] = 0;
+ }
+ switch ( depth )
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ fprintf( dest_doc, "<h%d>", depth );
+ // Only generate section numbers if no sectionnameonly
+ if ( !( course_of_action.do_sectionnameonly ) )
+ {
+ for ( i = 1; i <= depth; ++i )
+ {
+ fprintf( dest_doc, "%d.", section_counters[i] );
+ }
+ fprintf( dest_doc, " " );
+ }
+
+ // Print Header "first" name
+ RB_HTML_Generate_String( dest_doc, name );
+
+ // Print further names
+ for ( i = 1; i < header->no_names; i++ )
+ {
+ fprintf( dest_doc, ( i % header_breaks ) ? ", " : ",<br />" );
+ RB_HTML_Generate_String( dest_doc, header->names[i] );
+ }
+
+ // Include module name if not sectionnameonly
+ if ( !( course_of_action.do_sectionnameonly ) )
+ {
+ fprintf( dest_doc, " [ " );
+ RB_HTML_Generate_String( dest_doc, header->htype->indexName );
+ fprintf( dest_doc, " ]" );
+ }
+
+ fprintf( dest_doc, " </h%d>\n", depth );
+ break;
+ default:
+ /* too deep, don't do anything. */
+ assert( 0 );
+ }
+}
+
+void RB_HTML_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ USE( depth );
+ /* Empty */
+}
+
+
+char *RB_HTML_Get_Default_Extension(
+ void )
+{
+ return ( ".html" );
+}
+
+/****f* HTML_Generator/RB_HTML_Generate_Doc_Start
+ * NAME
+ * RB_HTML_Generate_Doc_Start --
+ * FUNCTION
+ * Generate the first part of a HTML document.
+ * As far as ROBODoc is concerned a HTML document
+ * consists of three parts:
+ * * The start of a document
+ * * The body of a document
+ * * The end of a document
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char *dest_name,
+ char *charset )
+/*
+ * INPUTS
+ * o dest_doc -- the output file.
+ * o src_name -- The file or directoryname from which
+ * this document is generated.
+ * o name -- The title for this document
+ * o dest_name -- the name of the output file.
+ * o charset -- the charset to be used for the file.
+ * SOURCE
+ */
+{
+
+ if ( course_of_action.do_headless )
+ {
+ /* The user wants a headless document, so we skip everything
+ * upto and until <BODY>
+ */
+ }
+ else
+ {
+ /* Append document type and title */
+ fprintf( dest_doc, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
+ charset ? charset : DEFAULT_CHARSET );
+ fprintf( dest_doc,
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" );
+ fprintf( dest_doc,
+ " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" );
+
+ fprintf( dest_doc,
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" );
+ fprintf( dest_doc, "<head>\n" );
+ fprintf( dest_doc,
+ "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />\n" );
+ /* TODO is charset still needed?? */
+ fprintf( dest_doc,
+ "<meta http-equiv=\"Content-type\" content=\"text/html; charset=%s\" />\n",
+ charset ? charset : DEFAULT_CHARSET );
+ RB_InsertCSS( dest_doc, dest_name );
+ fprintf( dest_doc, "<title>%s</title>\n", name );
+
+ /* append SGML-comment with document- and copyright-info. This code
+ * ensures that every line has an own comment to avoid problems with
+ * buggy browsers */
+ fprintf( dest_doc, "<!-- Source: %s -->\n", src_name );
+ if ( course_of_action.do_nogenwith )
+ {
+
+ }
+ else
+ {
+ static const char copyright_text[]
+ = COMMENT_ROBODOC /* COMMENT_COPYRIGHT */ ;
+ size_t i = 0;
+ char previous_char = '\n';
+ char current_char = copyright_text[i];
+
+ while ( current_char )
+ {
+ if ( previous_char == '\n' )
+ {
+ fprintf( dest_doc, "<!-- " );
+ }
+ if ( current_char == '\n' )
+ {
+ fprintf( dest_doc, " -->" );
+ }
+ else if ( ( current_char == '-' )
+ && ( previous_char == '-' ) )
+ {
+ /* avoid "--" inside SGML-comment, and use "-_" instead; this
+ * looks a bit strange, but one should still be able to figure
+ * out what is meant when reading the output */
+ current_char = '_';
+ }
+ fputc( current_char, dest_doc );
+ i += 1;
+ previous_char = current_char;
+ current_char = copyright_text[i];
+ }
+ }
+
+ /* append heading and start list of links to functions */
+ fprintf( dest_doc, "</head>\n" );
+ fprintf( dest_doc, "<body>\n" );
+ }
+
+// HTML_Generate_Div( dest_doc, "container" );
+
+ /* Generate document title if available (Thuffir) */
+ HTML_Generate_Div( dest_doc, "logo" );
+ fprintf( dest_doc, "<a name=\"robo_top_of_doc\">" );
+ if ( document_title )
+ RB_HTML_Generate_String( dest_doc, document_title );
+ fprintf( dest_doc, "</a>\n" );
+ HTML_Generate_Div_End( dest_doc, "logo" );
+
+
+
+}
+
+/******/
+
+
+/* TODO */
+/*x**if* HTML_Generator/RB_HTML_Generate_Doc_End
+ * NAME
+ * RB_HTML_Generate_Doc_End --
+ * FUNCTION
+ * Close of the document with the proper end tags.
+ ******
+ */
+
+void RB_HTML_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name,
+ char *src_name )
+{
+
+ USE( name );
+
+
+ HTML_Generate_Div( dest_doc, "footer" );
+ /* TODO This should be done with
+ * RB_Generate_Label()
+ */
+ if ( course_of_action.do_nogenwith )
+ {
+ fprintf( dest_doc, "<p>Generated from %s on ", src_name );
+ RB_TimeStamp( dest_doc );
+ fprintf( dest_doc, "</p>\n" );
+ }
+ else
+ {
+ fprintf( dest_doc,
+ "<p>Generated from %s with <a href=\"http://www.xs4all.nl/~rfsber/Robo/robodoc.html\">ROBODoc</a> V%s on ",
+ src_name, VERSION );
+ RB_TimeStamp( dest_doc );
+ fprintf( dest_doc, "</p>\n" );
+ }
+ HTML_Generate_Div_End( dest_doc, "footer" );
+
+// HTML_Generate_Div_End( dest_doc, "container" );
+
+ if ( course_of_action.do_footless )
+ {
+ /* The user does not want the foot of the
+ * document.
+ */
+ }
+ else
+ {
+ fprintf( dest_doc, "</body>\n</html>\n" );
+ }
+}
+
+
+void RB_HTML_Generate_Nav_Bar(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header )
+{
+ char *current_filename = NULL;
+ char *target_filename = NULL;
+ char *label = NULL;
+ char *label_name = NULL;
+
+ current_filename = RB_Get_FullDocname( current_header->owner->filename );
+ target_filename = RB_Get_FullDocname( current_header->owner->filename );
+ label = Get_Fullname( current_header->owner->filename );
+ /* The navigation bar */
+ fprintf( current_doc, "<p>" );
+
+ // [ Top ]
+ fprintf( current_doc, "[ " );
+ RB_HTML_Generate_Link( current_doc, current_filename, NULL,
+ "robo_top_of_doc", "Top", 0 );
+ fprintf( current_doc, " ] " );
+
+ // [ "Parentname" ]
+ if ( current_header->parent )
+ {
+ fprintf( current_doc, "[ " );
+ target_filename =
+ RB_Get_FullDocname( current_header->parent->owner->filename );
+ label = current_header->parent->unique_name;
+ label_name = current_header->parent->function_name;
+ RB_HTML_Generate_Link( current_doc, current_filename, target_filename,
+ label, label_name, 0 );
+ fprintf( current_doc, " ] " );
+ }
+
+ // [ "Modulename" ]
+ fprintf( current_doc, "[ " );
+ label_name = current_header->htype->indexName;
+ if ( ( course_of_action.do_index ) && ( course_of_action.do_multidoc ) )
+ {
+ target_filename = RB_Get_SubIndex_FileName( document->docroot->name,
+ document->extension,
+ current_header->htype );
+ RB_HTML_Generate_Link( current_doc, current_filename, target_filename,
+ "robo_top_of_doc", label_name, 0 );
+ free( target_filename );
+ }
+ else
+ {
+ RB_HTML_Generate_String( current_doc, label_name );
+ }
+ fprintf( current_doc, " ]</p>\n" );
+}
+
+
+
+/* TODO Documentation */
+
+void RB_HTML_Generate_Nav_Bar_One_File_Per_Header(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header )
+{
+ char *current_filename = NULL;
+ char *target_filename = NULL;
+ char *label = NULL;
+ char *label_name = NULL;
+
+ current_filename = RB_Get_FullDocname( current_header->owner->filename );
+ target_filename = RB_Get_FullDocname( current_header->owner->filename );
+ label = Get_Fullname( current_header->owner->filename );
+ /* The navigation bar */
+ if ( current_header->parent )
+ {
+ target_filename =
+ RB_Get_FullDocname( current_header->parent->owner->filename );
+ label = current_header->parent->unique_name;
+ label_name = current_header->parent->function_name;
+ RB_HTML_Generate_Link( current_doc, current_filename, target_filename,
+ label, label_name, "menuitem" );
+ }
+ /* FS TODO one_file_per_header without index is not logical */
+ if ( ( course_of_action.do_index ) && ( course_of_action.do_multidoc ) )
+ {
+ target_filename = RB_Get_SubIndex_FileName( document->docroot->name,
+ document->extension,
+ current_header->htype );
+ label_name = current_header->htype->indexName;
+ RB_HTML_Generate_Link( current_doc, current_filename, target_filename,
+ "robo_top_of_doc", label_name, "menuitem" );
+ free( target_filename );
+ }
+}
+
+
+
+
+
+/* TODO Documentation */
+/*x**if* HTML_Generator/RB_HTML_Generate_Header_Start
+ * NAME
+ * RB_HTML_Generate_Header_Start --
+ ******
+ */
+
+void RB_HTML_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ struct RB_HeaderType *header_type;
+ int i;
+
+ if ( cur_header->name && cur_header->function_name )
+ {
+ fprintf( dest_doc, "<hr />\n" );
+ RB_HTML_Generate_Label( dest_doc, cur_header->name );
+ fprintf( dest_doc, "</a><a name=\"%s\"></a><h2>",
+ cur_header->unique_name );
+
+ header_type = RB_FindHeaderType( cur_header->htype->typeCharacter );
+
+ for ( i = 1; i <= cur_header->no_names; i++ )
+ {
+ // If Section names only, do not print module name
+ if ( i == 1 && ( course_of_action.do_sectionnameonly ) )
+ RB_HTML_Generate_String( dest_doc,
+ cur_header->function_name );
+ else
+ RB_HTML_Generate_String( dest_doc, cur_header->names[i - 1] );
+
+ // Break lines after a predefined number of header names
+ if ( i < cur_header->no_names )
+ fprintf( dest_doc, ( i % header_breaks ) ? ", " : ",<br />" );
+ }
+
+ // Print header type (if available and not Section names only)
+ if ( header_type && !( course_of_action.do_sectionnameonly ) )
+ {
+ fprintf( dest_doc, " [ " );
+ RB_HTML_Generate_String( dest_doc, header_type->indexName );
+ fprintf( dest_doc, " ]" );
+ }
+
+ fprintf( dest_doc, "</h2>\n\n" );
+ }
+}
+
+/* TODO */
+/*x**f* HTML_Generator/RB_HTML_Generate_Header_End
+ * NAME
+ * RB_HTML_Generate_Header_End --
+ ******
+ */
+
+void RB_HTML_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+ fprintf( dest_doc, "\n" );
+}
+
+
+/****f* HTML_Generator/RB_HTML_Generate_IndexMenu
+ * FUNCTION
+ * Generates a menu to jump to the various master index files for
+ * the various header types. The menu is generated for each of the
+ * master index files. The current header type is highlighted.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_IndexMenu(
+ FILE *dest_doc,
+ char *filename,
+ struct RB_Document *document,
+ struct RB_HeaderType *cur_type )
+ /* TODO Use cur_type */
+/*
+ * INPUTS
+ * * dest_doc -- the output file.
+ * * filename -- the name of the output file
+ * * document -- the gathered documention.
+ * * cur_headertype -- the header type that is to be highlighted.
+ ******
+ */
+{
+ unsigned char type_char;
+ char *toc_index_path = NULL;
+
+ USE( cur_type ); /* TODO FS make use of this */
+ assert( dest_doc );
+ assert( filename );
+ assert( document );
+
+ toc_index_path = HTML_TOC_Index_Filename( document );
+ RB_HTML_Generate_Link( dest_doc,
+ filename,
+ toc_index_path,
+ "top", "Table of Contents", "menuitem" );
+ free( toc_index_path );
+ fprintf( dest_doc, "\n" );
+
+ for ( type_char = MIN_HEADER_TYPE;
+ type_char < MAX_HEADER_TYPE; ++type_char )
+ {
+ struct RB_HeaderType *header_type;
+ int n;
+
+ header_type = RB_FindHeaderType( type_char );
+ if ( header_type )
+ {
+ n = RB_Number_Of_Links( header_type, NULL, FALSE ) +
+ RB_Number_Of_Links( header_type, NULL, TRUE );
+
+ if ( n )
+ {
+ char *targetfilename = 0;
+
+ targetfilename =
+ RB_Get_SubIndex_FileName( document->docroot->name,
+ document->extension,
+ header_type );
+ assert( targetfilename );
+
+ RB_HTML_Generate_Link( dest_doc,
+ filename,
+ targetfilename,
+ "top",
+ header_type->indexName, "menuitem" );
+ free( targetfilename );
+ fprintf( dest_doc, "\n" );
+ }
+ }
+ }
+}
+
+/****f* HTML_Generator/RB_HTML_Generate_Index_Page
+ * FUNCTION
+ * Generate a single file with a index table for headers
+ * of one specific type of headers
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Index_Page(
+ struct RB_Document *document,
+ struct RB_HeaderType *header_type )
+/*
+ * INPUTS
+ * o document -- the document
+ * o header_type -- the type for which the table is to
+ * be generated.
+ ******
+ */
+{
+ char *filename = 0;
+ FILE *file;
+
+ assert( document );
+ assert( header_type );
+
+ filename = RB_Get_SubIndex_FileName( document->docroot->name,
+ document->extension, header_type );
+ assert( filename );
+
+ file = fopen( filename, "w" );
+ if ( !file )
+ {
+ RB_Panic( "can't open (%s)!\n", filename );
+ }
+ else
+ {
+ /* File opened, now we generate an index
+ * for the specified header type
+ */
+ RB_HTML_Generate_Doc_Start( file,
+ document->srcroot->name,
+ header_type->indexName,
+ filename, document->charset );
+
+ /* breadcrumbtrail */
+ HTML_Generate_Begin_Extra( file );
+ /* No content for extra section yet... */
+ HTML_Generate_End_Extra( file );
+
+ /* Menu for navigation */
+ HTML_Generate_Begin_Navigation( file );
+ RB_HTML_Generate_IndexMenu( file, filename, document, header_type );
+ HTML_Generate_End_Navigation( file );
+
+ /* Content */
+ HTML_Generate_Begin_Content( file );
+ if ( RB_CompareHeaderTypes
+ ( header_type, RB_FindHeaderType( HT_SOURCEHEADERTYPE ) )
+ && ( header_type->typeCharacter != HT_MASTERINDEXTYPE ) )
+ {
+ RB_HTML_Generate_Source_Tree( file, filename, document );
+ }
+ else
+ {
+ RB_HTML_Generate_Index_Table( file,
+ filename,
+ header_type,
+ header_type->indexName );
+ }
+ HTML_Generate_End_Content( file );
+ RB_HTML_Generate_Doc_End( file, filename, document->srcroot->name );
+ fclose( file );
+ }
+
+ free( filename );
+}
+
+
+/* Create an index page that contains only the table of content */
+
+void HTML_Generate_TOC_Index_Page(
+ struct RB_Document *document )
+{
+ FILE *file = NULL;
+ char *toc_index_path = HTML_TOC_Index_Filename( document );
+
+ file = fopen( toc_index_path, "w" );
+ if ( !file )
+ {
+ RB_Panic( "can't open (%s)!\n", toc_index_path );
+ }
+ else
+ {
+ RB_HTML_Generate_Doc_Start( file, document->srcroot->name,
+ "Table of Contents",
+ toc_index_path, document->charset );
+
+ /* breadcrumbtrail */
+ HTML_Generate_Begin_Extra( file );
+ /* No content for extra section yet... */
+ HTML_Generate_End_Extra( file );
+
+ /* Menu for navigation */
+ HTML_Generate_Begin_Navigation( file );
+ RB_HTML_Generate_IndexMenu( file, toc_index_path, document, NULL );
+ HTML_Generate_End_Navigation( file );
+
+ /* Content */
+ HTML_Generate_Begin_Content( file );
+ RB_Generate_TOC_2( file, document->headers,
+ document->no_headers, NULL, toc_index_path );
+ HTML_Generate_End_Content( file );
+
+ /* End part */
+ RB_HTML_Generate_Doc_End( file, toc_index_path,
+ document->srcroot->name );
+ fclose( file );
+ }
+
+ free( toc_index_path );
+}
+
+
+
+
+
+/* TODO */
+/*x**if* HTML_Generator/RB_HTML_Generate_Index
+ * NAME
+ * RB_HTML_Generate_Index --
+ ******
+ */
+
+
+/* Should be called indexes */
+void RB_HTML_Generate_Index(
+ struct RB_Document *document )
+{
+ unsigned char type_char = 0;
+
+ assert( document );
+
+ for ( type_char = MIN_HEADER_TYPE;
+ type_char < MAX_HEADER_TYPE; ++type_char )
+ {
+ struct RB_HeaderType *header_type;
+ int n;
+
+ header_type = RB_FindHeaderType( type_char );
+ if ( header_type )
+ {
+ n = RB_Number_Of_Links( header_type, NULL, FALSE ) +
+ RB_Number_Of_Links( header_type, NULL, TRUE );
+ if ( n )
+ {
+ /* There are headers of this type, so create an index page
+ * for them
+ */
+ RB_HTML_Generate_Index_Page( document, header_type );
+ }
+ }
+ }
+
+ RB_HTML_Generate_Index_Page( document,
+ RB_FindHeaderType( HT_MASTERINDEXTYPE ) );
+
+ HTML_Generate_TOC_Index_Page( document );
+}
+
+
+
+
+
+
+void RB_HTML_Generate_Table_Body(
+ FILE *dest,
+ char *dest_name,
+ struct RB_HeaderType *type,
+ int internal )
+{
+ struct RB_link *cur_link;
+ unsigned int i;
+ char first_char = ' ';
+ int found = FALSE;
+
+ /* Compute the number of columns we need for
+ * this type of header.
+ */
+ for ( i = 0; i < link_index_size; ++i )
+ {
+ cur_link = link_index[i];
+ if ( cur_link->htype &&
+ RB_CompareHeaderTypes( cur_link->htype, type ) &&
+ ( ( cur_link->is_internal && internal ) ||
+ ( !cur_link->is_internal && !internal ) ) )
+ {
+ char *r = 0;
+
+ r = RB_HTML_RelativeAddress( dest_name, cur_link->file_name );
+ if ( toupper( cur_link->object_name[0] ) != first_char )
+ {
+ first_char = toupper( cur_link->object_name[0] );
+ if ( found )
+ {
+// fprintf( dest, "</div>\n" );
+ }
+ fprintf( dest, "<h2><a name=\"%c\"></a>", first_char );
+ RB_HTML_Generate_Char( dest, first_char );
+ fprintf( dest, "</h2>" );
+// fprintf( dest, "<div class=\"indexitem\">\n" );
+ found = TRUE;
+ }
+ fprintf( dest, "<a href=\"%s#%s\" class=\"indexitem\" >", r,
+ cur_link->label_name );
+ RB_HTML_Generate_String( dest, cur_link->object_name );
+ fprintf( dest, "</a>\n" );
+ }
+ }
+ if ( found )
+ {
+// fprintf( dest, "</div>\n" );
+ }
+}
+
+
+/****if* HTML_Generator/RB_HTML_Generate_Index_Shortcuts
+ * NAME
+ * RB_HTML_Generate_Index_Shortcuts
+ * FUNCTION
+ * Generates alphabetic shortcuts to index entries.
+ * SYNOPSIS
+ */
+static void RB_HTML_Generate_Index_Shortcuts(
+ FILE *dest )
+/*
+ * INPUTS
+ * o dest -- the file to write to
+ * TODO
+ * - Only list used letters.
+ * - List all letters (accented, signs, etc), not just the common ones.
+ * - Should be better to implement it as a <div> ?
+ * SOURCE
+ */
+{
+ unsigned char c;
+
+ fprintf( dest, "<h2>" );
+
+ // Generate A - Z
+ for ( c = 'A'; c <= 'Z'; c++ )
+ {
+ fprintf( dest, "<a href=\"#%c\">", c );
+ RB_HTML_Generate_Char( dest, c );
+ fprintf( dest, "</a> - " );
+ }
+
+ // Generate 0 - 9
+ for ( c = '0'; c <= '9'; c++ )
+ {
+ fprintf( dest, "<a href=\"#%c\">", c );
+ RB_HTML_Generate_Char( dest, c );
+ fprintf( dest, "</a>" );
+
+ // Do not generate separator for the last char
+ if ( c != '9' )
+ {
+ fprintf( dest, " - " );
+ }
+ }
+
+ fprintf( dest, "</h2>\n" );
+}
+
+/********/
+
+/****if* HTML_Generator/RB_HTML_Generate_Index_Table
+ * NAME
+ * RB_HTML_Generate_Index_Table --
+ * FUNCTION
+ * Create a HTML TABLE containing links to headers of a particular
+ * type. This creates two tables, a table for normal headers as
+ * well as one for internal headers.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Index_Table(
+ FILE *dest,
+ char *dest_name,
+ struct RB_HeaderType *type,
+ char *title )
+/*
+ * INPUTS
+ * o dest -- the file in which to write the table
+ * o dest_name -- the name of this file
+ * o type -- the type of header for which to generate
+ * the table
+ * o title -- the title of the table.
+ * SOURCE
+ */
+{
+ /* Compute the number of columns we need for
+ * this type of header.
+ */
+
+ // Generate Index Title
+ fprintf( dest, "<h1>" );
+ RB_HTML_Generate_String( dest, title );
+ fprintf( dest, "</h1>\n" );
+
+ // Generate Shortcuts at the begining
+ RB_HTML_Generate_Index_Shortcuts( dest );
+
+ if ( RB_Number_Of_Links( type, NULL, FALSE ) )
+ {
+ if ( RB_Number_Of_Links( type, NULL, TRUE ) )
+ {
+ /* only print a title if there are two tables. */
+ fprintf( dest, "<h2>Normal</h2>" );
+ }
+ RB_HTML_Generate_Table_Body( dest, dest_name, type, FALSE );
+ }
+
+ if ( RB_Number_Of_Links( type, NULL, TRUE ) )
+ {
+ /* Always print the Internal title, since
+ * these headers are special and the user should know
+ * he is looking at something special.
+ */
+ fprintf( dest, "<h2>Internal</h2>" );
+ RB_HTML_Generate_Table_Body( dest, dest_name, type, TRUE );
+ }
+
+ // Generate Shortcuts at the end
+ RB_HTML_Generate_Index_Shortcuts( dest );
+}
+
+/********/
+
+
+
+/* TODO */
+/*x**if* HTML_Generator/RB_HTML_Generate_Empty_Item
+ * NAME
+ * RB_HTML_Generate_Empty_Item --
+ ******
+ */
+
+void RB_HTML_Generate_Empty_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<br>\n" );
+}
+
+
+
+
+/****f* HTML_Generator/RB_HTML_Generate_Link
+ * NAME
+ * RB_HTML_Generate_Link --
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Link(
+ FILE *cur_doc,
+ char *cur_name,
+ char *filename,
+ char *labelname,
+ char *linkname,
+ char *classname )
+/*
+ * INPUTS
+ * cur_doc -- the file to which the text is written
+ * cur_name -- the name of the destination file
+ * (the file from which we link)
+ * filename -- the name of the file that contains the link
+ * (the file we link to)
+ * labelname-- the name of the unique label of the link.
+ * linkname -- the name of the link as shown to the user.
+ * SOURCE
+ */
+{
+ if ( classname )
+ {
+ fprintf( cur_doc, "<a class=\"%s\" ", classname );
+ }
+ else
+ {
+ fprintf( cur_doc, "<a " );
+ }
+ if ( filename && strcmp( filename, cur_name ) )
+ {
+ char *r = RB_HTML_RelativeAddress( cur_name, filename );
+
+ fprintf( cur_doc, "href=\"%s#%s\">", r, labelname );
+ RB_HTML_Generate_String( cur_doc, linkname );
+ fprintf( cur_doc, "</a>" );
+
+ }
+ else
+ {
+ fprintf( cur_doc, "href=\"#%s\">", labelname );
+ RB_HTML_Generate_String( cur_doc, linkname );
+ fprintf( cur_doc, "</a>" );
+ }
+}
+
+/******/
+
+
+/****f* HTML_Generator/RB_HTML_RelativeAddress
+ * FUNCTION
+ * Link to 'that' from 'this' computing the relative path. Here
+ * 'this' and 'that' are both paths. This function is used to
+ * create links from one document to another document that might be
+ * in a completely different directory.
+ * SYNOPSIS
+ */
+char *RB_HTML_RelativeAddress(
+ char *thisname,
+ char *thatname )
+/*
+ * EXAMPLE
+ * The following two
+ * this /sub1/sub2/sub3/f.html
+ * that /sub1/sub2/g.html
+ * result in
+ * ../g.html
+ *
+ * this /sub1/f.html
+ * that /sub1/sub2/g.html
+ * ==
+ * ./sub2/g.html
+ *
+ * this /sub1/f.html
+ * that /sub1/g.html
+ * ==
+ * ./g.html
+ *
+ * this /sub1/doc3/doc1/tt.html
+ * that /sub1/doc5/doc2/qq.html
+ * ==
+ * ../../doc5/doc2/qq.html
+ *
+ * NOTES
+ * Notice the execelent docmentation.
+ * SOURCE
+ */
+#define MAX_RELATIVE_SIZE 1024
+{
+ static char relative[MAX_RELATIVE_SIZE + 1];
+ char *i_this;
+ char *i_that;
+ char *i_this_slash = NULL;
+ char *i_that_slash = NULL;
+
+ relative[0] = '\0';
+
+ assert( thisname );
+ assert( thatname );
+
+ for ( i_this = thisname, i_that = thatname;
+ ( *i_this && *i_that ) && ( *i_this == *i_that );
+ ++i_this, ++i_that )
+ {
+ if ( *i_this == '/' )
+ {
+ i_this_slash = i_this;
+ }
+ if ( *i_that == '/' )
+ {
+ i_that_slash = i_that;
+ }
+ }
+
+ if ( i_this_slash && i_that_slash )
+ {
+ int this_slashes_left = 0;
+ int that_slashes_left = 0;
+ char *i_c;
+
+ for ( i_c = i_this_slash + 1; *i_c; ++i_c )
+ {
+ if ( *i_c == '/' )
+ {
+ ++this_slashes_left;
+ }
+ }
+
+ for ( i_c = i_that_slash + 1; *i_c; ++i_c )
+ {
+ if ( *i_c == '/' )
+ {
+ ++that_slashes_left;
+ }
+ }
+
+ if ( this_slashes_left )
+ {
+ int i;
+
+ for ( i = 0; i < this_slashes_left; ++i )
+ {
+ strcat( relative, "../" );
+ }
+ strcat( relative, i_that_slash + 1 );
+ }
+ else if ( that_slashes_left )
+ {
+ /* !this_slashes_left && that_slashes_left */
+ strcat( relative, "./" );
+ strcat( relative, i_that_slash + 1 );
+ }
+ else
+ {
+ /* !this_slashes_left && !that_slashes_left */
+ strcat( relative, "./" );
+ strcat( relative, i_that_slash + 1 );
+ }
+ }
+ return relative;
+}
+
+/******/
+
+
+
+/****f* HTML_Generator/RB_HTML_Generate_Char
+ * NAME
+ * RB_HTML_Generate_Char -- generate a single character for an item.
+ * SYNOPSIS
+ */
+void RB_HTML_Generate_Char(
+ FILE *dest_doc,
+ int c )
+/*
+ * FUNCTION
+ * This function is called for every character that goes
+ * into an item's body. This escapes all the reserved
+ * HTML characters such as '&', '<', '>', '"'.
+ * SOURCE
+ */
+{
+ switch ( c )
+ {
+ case '\n':
+ assert( 0 );
+ break;
+ case '\t':
+ assert( 0 );
+ break;
+ case '<':
+ fprintf( dest_doc, "<" );
+ break;
+ case '>':
+ fprintf( dest_doc, ">" );
+ break;
+ case '&':
+ fprintf( dest_doc, "&" );
+ break;
+ default:
+ // All others are printed literally
+ fputc( c, dest_doc );
+ }
+}
+
+/*******/
+
+
+void HTML_Generate_Begin_Content(
+ FILE *dest_doc )
+{
+ HTML_Generate_Div( dest_doc, "content" );
+}
+
+void HTML_Generate_End_Content(
+ FILE *dest_doc )
+{
+ HTML_Generate_Div_End( dest_doc, "content" );
+}
+
+void HTML_Generate_Begin_Navigation(
+ FILE *dest_doc )
+{
+ HTML_Generate_Div( dest_doc, "navigation" );
+}
+
+void HTML_Generate_End_Navigation(
+ FILE *dest_doc )
+{
+
+ HTML_Generate_Div_End( dest_doc, "navigation" );
+}
+
+void HTML_Generate_Begin_Extra(
+ FILE *dest_doc )
+{
+ HTML_Generate_Div( dest_doc, "extra" );
+}
+
+void HTML_Generate_End_Extra(
+ FILE *dest_doc )
+{
+ HTML_Generate_Div_End( dest_doc, "extra" );
+}
+
+
+
+
+/****f* HTML_Generator/RB_Create_CSS
+ * FUNCTION
+ * Create the .css file. Unless the user specified it's own css
+ * file robodoc creates a default one.
+ *
+ * For multidoc mode the name of the .css file is
+ * robodoc.css
+ * For singledoc mode the name of the .css file is equal
+ * to the name of the documentation file.
+ * SYNOPSIS
+ */
+void RB_Create_CSS(
+ struct RB_Document *document )
+/*
+ * INPUTS
+ * o document -- the document for which to create the file.
+ * SOURCE
+ */
+{
+ size_t l = 0;
+ FILE *css_file;
+
+ /* compute the complete path to the css file */
+ if ( ( document->actions.do_singledoc ) ||
+ ( document->actions.do_singlefile ) )
+ {
+ char *extension = ".css";
+
+ l += strlen( document->singledoc_name );
+ l += strlen( extension );
+ ++l;
+ css_name = malloc( l );
+ strcpy( css_name, document->singledoc_name );
+ strcat( css_name, extension );
+ }
+ else
+ {
+ struct RB_Path *docroot = document->docroot;
+ char *docrootname = docroot->name;
+ char *filename = "robodoc.css";
+
+ l = strlen( filename );
+ l += strlen( docrootname );
+ ++l;
+ css_name = malloc( l );
+ strcpy( css_name, docrootname );
+ strcat( css_name, filename );
+ }
+
+ RB_Say( "Creating CSS file %s\n", SAY_DEBUG, css_name );
+ if ( document->css )
+ {
+ /* The user specified its own css file,
+ * so we use the content of that.
+ */
+ RB_CopyFile( document->css, css_name );
+ }
+ else
+ {
+ css_file = fopen( css_name, "w" );
+ if ( css_file )
+ {
+ /** BEGIN BEGIN BEGIN Don't remove */
+ fprintf( css_file,
+ "/****h* ROBODoc/ROBODoc Cascading Style Sheet\n"
+ " * FUNCTION\n"
+ " * This is the default cascading style sheet for documentation\n"
+ " * generated with ROBODoc.\n"
+ " * You can edit this file to your own liking and then use\n"
+ " * it with the option\n"
+ " * --css <filename>\n"
+ " *\n"
+ " * This style-sheet defines the following layout\n"
+ " * +----------------------------------------+\n"
+ " * | logo |\n"
+ " * +----------------------------------------+\n"
+ " * | extra |\n"
+ " * +----------------------------------------+\n"
+ " * | | navi- |\n"
+ " * | | gation |\n"
+ " * | content | |\n"
+ " * | | |\n"
+ " * +----------------------------------------+\n"
+ " * | footer |\n"
+ " * +----------------------------------------+\n"
+ " *\n"
+ " * This style-sheet is based on a style-sheet that was automatically\n"
+ " * generated with the Strange Banana stylesheet generator.\n"
+ " * See http://www.strangebanana.com/generator.aspx\n"
+ " *\n"
+ " ******\n"
+ " * $Id: html_generator.c,v 1.91 2007/07/10 19:13:51 gumpu Exp $\n"
+ " */\n"
+ "\n"
+ "body\n"
+ "{\n"
+ " background-color: rgb(255,255,255);\n"
+ " color: rgb(98,84,55);\n"
+ " font-family: Arial, serif;\n"
+ " border-color: rgb(226,199,143);\n"
+ "}\n"
+ "\n"
+ "pre\n"
+ "{\n"
+ " font-family: monospace;\n"
+ " margin: 15px;\n"
+ " padding: 5px;\n"
+ " white-space: pre;\n"
+ " color: #000;\n"
+ "}\n"
+ "\n"
+ "pre.source\n"
+ "{\n"
+ " background-color: #ffe;\n"
+ " border: dashed #aa9 1px;\n"
+ "}\n"
+ "\n"
+ "p\n"
+ "{\n"
+ " margin:15px;\n"
+ "}\n"
+ "\n"
+ "p.item_name \n"
+ "{\n"
+ " font-weight: bolder;\n"
+ " margin:5px;\n"
+ " font-size: 120%%;\n"
+ "}\n"
+ "\n"
+ "#content\n" "{\n" " font-size: 100%%;\n" );
+ fprintf( css_file,
+ " color: rgb(0,0,0);\n"
+ " background-color: rgb(255,255,255);\n"
+ " border-left-width: 0px; \n"
+ " border-right-width: 0px; \n"
+ " border-top-width: 0px; \n"
+ " border-bottom-width: 0px;\n"
+ " border-left-style: none; \n"
+ " border-right-style: none; \n"
+ " border-top-style: none; \n"
+ " border-bottom-style: none;\n"
+ " padding: 40px 31px 14px 17px;\n"
+ " border-color: rgb(0,0,0);\n"
+ " text-align: justify;\n"
+ "}\n"
+ "\n"
+ "#navigation\n"
+ "{\n"
+ " background-color: rgb(98,84,55);\n"
+ " color: rgb(230,221,202);\n"
+ " font-family: \"Times New Roman\", serif;\n"
+ " font-style: normal;\n"
+ " border-color: rgb(0,0,0);\n"
+ "}\n"
+ "\n"
+ "a.menuitem\n"
+ "{\n"
+ " font-size: 120%%;\n"
+ " background-color: rgb(0,0,0);\n"
+ " color: rgb(195,165,100);\n"
+ " font-variant: normal;\n"
+ " text-transform: none;\n"
+ " font-weight: normal;\n"
+ " padding: 1px 8px 3px 1px;\n"
+ " margin-left: 5px; \n"
+ " margin-right: 5px; \n"
+ " margin-top: 5px; \n"
+ " margin-bottom: 5px;\n"
+ " border-color: rgb(159,126,57);\n"
+ " text-align: right;\n"
+ "}\n"
+ "\n"
+ "#logo, #logo a\n"
+ "{\n"
+ " font-size: 130%%;\n"
+ " background-color: rgb(198,178,135);\n"
+ " color: rgb(98,84,55);\n"
+ " font-family: Georgia, serif;\n"
+ " font-style: normal;\n"
+ " font-variant: normal;\n"
+ " text-transform: none;\n"
+ " font-weight: bold;\n"
+ " padding: 20px 18px 20px 18px;\n"
+ " border-color: rgb(255,255,255);\n"
+ " text-align: right;\n"
+ "}\n"
+ "\n"
+ "#extra, #extra a\n"
+ "{\n"
+ " font-size: 128%%;\n"
+ " background-color: rgb(0,0,0);\n"
+ " color: rgb(230,221,202);\n"
+ " font-style: normal;\n"
+ " font-variant: normal;\n"
+ " text-transform: none;\n"
+ " font-weight: normal;\n" );
+ fprintf( css_file,
+ " border-left-width: 0px; \n"
+ " border-right-width: 0px; \n"
+ " border-top-width: 0px; \n"
+ " border-bottom-width: 0px;\n"
+ " border-left-style: none; \n"
+ " border-right-style: none; \n"
+ " border-top-style: none; \n"
+ " border-bottom-style: none;\n"
+ " padding: 12px 12px 12px 12px;\n"
+ " border-color: rgb(195,165,100);\n"
+ " text-align: center;\n"
+ "}\n"
+ "\n"
+ "#content a\n"
+ "{\n"
+ " color: rgb(159,126,57);\n"
+ " text-decoration: none;\n"
+ "}\n"
+ "\n"
+ "#content a:hover, #content a:active\n"
+ "{\n"
+ " color: rgb(255,255,255);\n"
+ " background-color: rgb(159,126,57);\n"
+ "}\n"
+ "\n"
+ "a.indexitem\n"
+ "{\n"
+ " display: block;\n"
+ "}\n"
+ "\n"
+ "h1, h2, h3, h4, h5, h6\n"
+ "{\n"
+ " background-color: rgb(221,221,221);\n"
+ " font-family: Arial, serif;\n"
+ " font-style: normal;\n"
+ " font-variant: normal;\n"
+ " text-transform: none;\n"
+ " font-weight: normal;\n"
+ "}\n"
+ "\n"
+ "h1\n"
+ "{\n"
+ " font-size: 151%%;\n"
+ "}\n"
+ "\n"
+ "h2\n"
+ "{\n"
+ " font-size: 142%%;\n"
+ "}\n"
+ "\n"
+ "h3\n"
+ "{\n"
+ " font-size: 133%%;\n"
+ "}\n"
+ "\n"
+ "h4\n"
+ "{\n"
+ " font-size: 124%%;\n"
+ "}\n"
+ "\n"
+ "h5\n"
+ "{\n"
+ " font-size: 115%%;\n"
+ "}\n"
+ "\n"
+ "h6\n"
+ "{\n"
+ " font-size: 106%%;\n"
+ "}\n"
+ "\n"
+ "#navigation a\n"
+ "{\n"
+ " text-decoration: none;\n"
+ "}\n"
+ "\n"
+ ".menuitem:hover\n"
+ "{\n"
+ " background-color: rgb(195,165,100);\n"
+ " color: rgb(0,0,0);\n"
+ "}\n"
+ "\n"
+ "#extra a\n"
+ "{\n"
+ " text-decoration: none;\n"
+ "}\n"
+ "\n"
+ "#logo a\n"
+ "{\n"
+ " text-decoration: none;\n"
+ "}\n"
+ "\n"
+ "#extra a:hover\n"
+ "{\n"
+ "}\n"
+ "\n"
+ "/* layout */\n"
+ "#navigation\n"
+ "{\n"
+ " width: 22%%; \n"
+ " position: relative; \n"
+ " top: 0; \n"
+ " right: 0; \n"
+ " float: right; \n"
+ " text-align: center;\n"
+ " margin-left: 10px;\n"
+ "}\n"
+ "\n"
+ ".menuitem {width: auto;}\n"
+ "#content {width: auto;}\n"
+ ".menuitem {display: block;}\n" "\n" "\n" );
+ fprintf( css_file,
+ "div#footer\n"
+ "{\n"
+ " background-color: rgb(198,178,135);\n"
+ " color: rgb(98,84,55);\n"
+ " clear: left;\n"
+ " width: 100%%;\n"
+ " font-size: 71%%;\n"
+ "}\n"
+ "\n"
+ "div#footer a\n"
+ "{\n"
+ " background-color: rgb(198,178,135);\n"
+ " color: rgb(98,84,55);\n"
+ "}\n"
+ "\n"
+ "div#footer p\n"
+ "{\n"
+ " margin:0;\n"
+ " padding:5px 10px\n"
+ "}\n"
+ "\n"
+ "span.keyword\n"
+ "{\n"
+ " color: #00F;\n"
+ "}\n"
+ "\n"
+ "span.comment\n"
+ "{\n"
+ " color: #080;\n"
+ "}\n"
+ "\n"
+ "span.quote\n"
+ "{\n"
+ " color: #F00;\n"
+ "}\n"
+ "\n"
+ "span.squote\n"
+ "{\n"
+ " color: #F0F;\n"
+ "}\n"
+ "\n"
+ "span.sign\n"
+ "{\n"
+ " color: #008B8B;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "@media print\n"
+ "{\n"
+ " #navigation {display: none;}\n"
+ " #content {padding: 0px;}\n"
+ " #content a {text-decoration: underline;}\n"
+ "}\n" );
+ /** END END END Don't remove */
+ fclose( css_file );
+ }
+ else
+ {
+ RB_Panic( "Can't open %s for writing\n", css_name );
+ }
+ }
+}
+
+/*******/
+
+
+void RB_InsertCSS(
+ FILE *dest_doc,
+ char *filename )
+{
+ if ( css_name )
+ {
+ char *r = RB_HTML_RelativeAddress( filename, css_name );
+
+ assert( r );
+ assert( strlen( r ) );
+ fprintf( dest_doc,
+ "<link rel=\"stylesheet\" href=\"%s\" type=\"text/css\" />\n",
+ r );
+ }
+}
+
+
+
+void HTML_Generate_Begin_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<p>" );
+}
+
+void HTML_Generate_End_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</p>\n" );
+}
+
+
+void HTML_Generate_Begin_Preformatted(
+ FILE *dest_doc,
+ int source )
+{
+ // Check if we are preformatting a SOURCE item
+ if ( source )
+ {
+ // SOURCE items have their own class in the CSS
+ fprintf( dest_doc, "<pre class=\"%s\">", SOURCE_CLASS );
+ }
+ else
+ {
+ fprintf( dest_doc, "<pre>" );
+ }
+}
+
+void HTML_Generate_End_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</pre>\n" );
+}
+
+
+void HTML_Generate_Begin_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<ul>" );
+}
+
+void HTML_Generate_End_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</ul>\n" );
+}
+
+void HTML_Generate_Begin_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<li>" );
+}
+
+void HTML_Generate_End_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</li>\n" );
+}
--- /dev/null
+#ifndef ROBODOC_HTML_GENERATOR_H
+#define ROBODOC_HTML_GENERATOR_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+#include "items.h"
+#include "document.h"
+
+#define MAX_SECTION_DEPTH 7
+
+void RB_HTML_Generate_Label(
+ FILE *dest_doc,
+ char *name );
+void RB_HTML_Generate_Item_Begin(
+ FILE *dest_doc,
+ char *name );
+void RB_HTML_Generate_Item_End(
+ FILE *dest_doc,
+ char *name );
+void RB_HTML_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count,
+ struct RB_Part *owner,
+ char *dest_name );
+void RB_HTML_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header );
+void RB_HTML_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+char *RB_HTML_Get_Default_Extension(
+ void );
+void RB_HTML_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char *dest_name,
+ char *charset );
+void RB_HTML_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name,
+ char *src_name );
+void RB_HTML_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_HTML_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+
+void RB_HTML_Generate_Index(
+ struct RB_Document *document );
+
+void RB_HTML_Generate_Index_Table(
+ FILE *dest,
+ char *dest_name,
+ struct RB_HeaderType *type,
+ char *title );
+
+void RB_HTML_Generate_Empty_Item(
+ FILE *dest );
+void RB_HTML_Generate_Link(
+ FILE *cur_doc,
+ char *cur_name,
+ char *filename,
+ char *labelname,
+ char *linkname,
+ char *classname );
+void RB_HTML_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_HTML_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+char *RB_HTML_RelativeAddress(
+ char *thisname,
+ char *thatname );
+
+int RB_HTML_Generate_Extra(
+ FILE *dest_doc,
+ enum ItemType item_type,
+ char *cur_char,
+ char prev_char );
+
+void RB_HTML_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+void RB_Create_CSS(
+ struct RB_Document *document );
+void RB_InsertCSS(
+ FILE *dest_doc,
+ char *filename );
+void RB_HTML_Generate_Nav_Bar(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header );
+
+void RB_HTML_Generate_Nav_Bar_One_File_Per_Header(
+ struct RB_Document *document,
+ FILE *current_doc,
+ struct RB_header *current_header );
+
+void HTML_Generate_Begin_Paragraph(
+ FILE *dest_doc );
+void HTML_Generate_End_Paragraph(
+ FILE *dest_doc );
+
+void HTML_Generate_Begin_Preformatted(
+ FILE *dest_doc,
+ int source );
+void HTML_Generate_End_Preformatted(
+ FILE *dest_doc );
+
+void HTML_Generate_Begin_List(
+ FILE *dest_doc );
+void HTML_Generate_End_List(
+ FILE *dest_doc );
+void HTML_Generate_Begin_List_Item(
+ FILE *dest_doc );
+void HTML_Generate_End_List_Item(
+ FILE *dest_doc );
+
+void HTML_Generate_Begin_Content(
+ FILE *dest_doc );
+void HTML_Generate_End_Content(
+ FILE *dest_doc );
+void HTML_Generate_Begin_Navigation(
+ FILE *dest_doc );
+void HTML_Generate_End_Navigation(
+ FILE *dest_doc );
+void HTML_Generate_Begin_Extra(
+ FILE *dest_doc );
+void HTML_Generate_End_Extra(
+ FILE *dest_doc );
+void RB_HTML_Generate_Line_Comment_End(
+ FILE *dest_doc );
+void RB_HTML_Generate_IndexMenu(
+ FILE *dest_doc,
+ char *filename,
+ struct RB_Document *document,
+ struct RB_HeaderType *cur_type );
+
+#endif /* ROBODOC_HTML_GENERATOR_H */
--- /dev/null
+// vi: ff=unix spell
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/Items
+ * FUNCTION
+ * This module contains functions that deal with items. The
+ * documentation consists of headers, and headers contains one of
+ * more items. Each item has a name and a body. All possible items
+ * are listed in configuration.items. A uses can specify that
+ * certain items are not to be added to the documentation. These
+ * items are listed in configuration.ignore_items.
+ * AUTHOR
+ * Frans Slothouber
+ *******
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "globals.h"
+#include "robodoc.h"
+#include "items.h"
+#include "roboconfig.h"
+#include "util.h"
+
+/****v* Items/item_name_buffer
+ * FUNCTION
+ * Stores the name of the last item that was found.
+ * SOURCE
+ */
+
+#define MAX_ITEM_NAME_LENGTH 1024
+char item_name_buffer[MAX_ITEM_NAME_LENGTH];
+
+/*****/
+
+/* TODO Documentation */
+char *RB_Get_Item_Name(
+ void )
+{
+ return item_name_buffer;
+}
+
+/****f* Items/RB_Create_Item
+ *
+ * SOURCE
+ */
+
+struct RB_Item *RB_Create_Item(
+ enum ItemType arg_item_type )
+{
+ struct RB_Item *item = malloc( sizeof( struct RB_Item ) );
+
+ assert( item );
+
+ item->next = 0;
+ item->type = arg_item_type;
+ item->begin_index = 0;
+ item->end_index = 0;
+
+ return item;
+}
+
+/*****/
+
+/****f* Items/RB_Get_Item_Type [3.0b]
+ * FUNCTION
+ * return the item_type represented by the given string.
+ * SYNOPSIS
+ * int RB_Get_Item_Type( char *cmp_name )
+ * INPUTS
+ * char *cmp_name -- item_name to evaluate
+ * RESULT
+ * int -- the right item_type or NO_ITEM
+ * SOURCE
+ */
+
+int RB_Get_Item_Type(
+ char *cmp_name )
+{
+ unsigned int item_type;
+
+ assert( configuration.items.number );
+ for ( item_type = 0; item_type < configuration.items.number; ++item_type )
+ {
+ char *item = configuration.items.names[item_type];
+
+ /* Skip preformat mark */
+ if ( *item == '-' )
+ item++;
+ if ( !strcmp( item, cmp_name ) )
+ {
+ return ( item_type );
+ }
+ }
+ return ( NO_ITEM );
+}
+
+/*** RB_Get_Item_Type ***/
+
+
+
+/****f* Items/RB_Is_ItemName
+ * FUNCTION
+ * Is there an itemname in the line. Ignores leading spaces and
+ * remark markers.
+ * INPUTS
+ * line -- line to be searched.
+ * RESULT
+ * The kind of item that was found or NO_ITEM if no item could be found.
+ * The name of the item will be stored in item_name_buffer.
+ * NOTES
+ * We used to check for misspelled items names by testing if
+ * the item name buffer consists of only upper case characters.
+ * However checking for a misspelled item name this way results in
+ * many false positives. For instance many warnings are given for
+ * FORTRAN code as all the keywords are in uppercase. We need to
+ * find a better method for this.
+ * SOURCE
+ */
+
+enum ItemType RB_Is_ItemName(
+ char *line )
+{
+ char *cur_char = line;
+ int i = 0;
+
+ cur_char = RB_Skip_Whitespace( cur_char );
+ if ( RB_Has_Remark_Marker( cur_char ) )
+ {
+ cur_char = RB_Skip_Remark_Marker( cur_char );
+ cur_char = RB_Skip_Whitespace( cur_char );
+ /* It there anything left? */
+ if ( strlen( cur_char ) )
+ {
+ enum ItemType item_type = NO_ITEM;
+
+ /* Copy the name */
+ strcpy( item_name_buffer, cur_char );
+ /* remove any trailing spaces */
+ for ( i = strlen( item_name_buffer ) - 1;
+ i >= 0 && utf8_isspace( item_name_buffer[i] ); --i )
+ {
+ item_name_buffer[i] = '\0';
+ }
+ /* No check and see if this is an item name */
+ if ( strlen( item_name_buffer ) )
+ {
+ item_type = RB_Get_Item_Type( item_name_buffer );
+#if 0 /* Until we find a better method */
+ if ( item_type == NO_ITEM )
+ {
+ /* Check if it is a misspelled item name */
+ item_type = POSSIBLE_ITEM;
+ for ( i = 0; i < strlen( item_name_buffer ); ++i )
+ {
+ if ( !( utf8_isupper( item_name_buffer[i] ) ||
+ utf8_isspace( item_name_buffer[i] ) ) )
+ {
+ /* No it is not */
+ item_type = NO_ITEM;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ return item_type;
+ }
+ else
+ {
+ return NO_ITEM;
+ }
+ }
+ else
+ {
+ return NO_ITEM;
+ }
+}
+
+/******/
+
+/* TODO Documentation */
+int Is_Ignore_Item(
+ char *name )
+{
+ unsigned int i;
+
+ for ( i = 0; i < configuration.ignore_items.number; ++i )
+ {
+ if ( !strcmp( configuration.ignore_items.names[i], name ) )
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/****f* HeaderTypes/Works_Like_SourceItem
+ * FUNCTION
+ * Tells wether this item works similar to the
+ * source item, that is weather it copies it's
+ * content verbatim to the output document.
+ * SYNPOPSIS
+ */
+int Works_Like_SourceItem(
+ enum ItemType item_type )
+/*
+ * INPUTS
+ * item_type -- Type of item (also the index to the item name)
+ * RESULT
+ * TRUE -- Item works like a SOURCE item
+ * FALSE -- Item does NOT work like a SOURCE item
+ * SOURCE
+ */
+{
+ unsigned int i;
+
+ // Check if it is a SOURCE item
+ if ( item_type == SOURCECODE_ITEM )
+ {
+ return TRUE;
+ }
+
+ // Lookup if it works like a SOURCE item
+ for ( i = 0; i < configuration.source_items.number; ++i )
+ {
+ if ( !strcmp
+ ( configuration.source_items.names[i],
+ configuration.items.names[item_type] ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // Neither SOURCE item, nor works like it
+ return FALSE;
+}
+
+/******/
+
+/****f* HeaderTypes/Is_Preformatted_Item
+ * FUNCTION
+ * Tells wether this item should be automatically preformatted in the
+ * output.
+ * SYNPOPSIS
+ */
+int Is_Preformatted_Item(
+ enum ItemType item_type )
+/*
+ * INPUTS
+ * item_type -- Type of item (also the index to the item name)
+ * RESULT
+ * TRUE -- Item should be automatically preformatted
+ * FALSE -- Item should NOT be automatically preformatted
+ * SOURCE
+ */
+{
+ unsigned int i;
+
+ // Lookup if item should be preformatted
+ for ( i = 0; i < configuration.preformatted_items.number; ++i )
+ {
+ if ( !strcmp
+ ( configuration.preformatted_items.names[i],
+ configuration.items.names[item_type] ) )
+ {
+ // Item name match, it sould be preformatted
+ return TRUE;
+ }
+ }
+
+ // Do not automatically preformat item
+ return FALSE;
+}
+
+/******/
+
+/****f* HeaderTypes/Is_Format_Item
+ * FUNCTION
+ * Tells wether this item should be formatted by the browser
+ * SYNPOPSIS
+ */
+int Is_Format_Item(
+ enum ItemType item_type )
+/*
+ * INPUTS
+ * item_type -- Type of item (also the index to the item name)
+ * RESULT
+ * TRUE -- Item should be formatted by the browser
+ * FALSE -- Item should be left alone
+ * SOURCE
+ */
+{
+ unsigned int i;
+
+ // Lookup if item should be formatted by the browser
+ for ( i = 0; i < configuration.format_items.number; ++i )
+ {
+ if ( !strcmp
+ ( configuration.format_items.names[i],
+ configuration.items.names[item_type] ) )
+ {
+ // Item name match, it sould be formatted by the browser
+ return TRUE;
+ }
+ }
+
+ // Leave item alone
+ return FALSE;
+}
+
+/******/
--- /dev/null
+#ifndef ROBODOC_ITEMS_H
+#define ROBODOC_ITEMS_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "robodoc.h"
+
+/****t* Items/ItemTypes
+ * NAME
+ * ItemTypes -- enumeration of item types
+ * FUNCTION
+ * Defines a number of item types. There are two kind of items:
+ * * the SOURCE item which is always available,
+ * * and items defined by the user (or through the default items).
+ * NOTES
+ * Never check an item type against SOURCECODE_ITEM directily!
+ * Use Works_Like_SourceItem() function instead.
+ * SOURCE
+ */
+
+enum ItemType
+{ POSSIBLE_ITEM = -2, NO_ITEM = -1, SOURCECODE_ITEM = 0, OTHER_ITEM };
+
+/*****/
+
+
+#define RBILA_BEGIN_PARAGRAPH ( 1 << 1 )
+#define RBILA_END_PARAGRAPH ( 1 << 2 )
+#define RBILA_BEGIN_LIST ( 1 << 3 )
+#define RBILA_END_LIST ( 1 << 4 )
+#define RBILA_BEGIN_LIST_ITEM ( 1 << 5 )
+#define RBILA_END_LIST_ITEM ( 1 << 6 )
+#define RBILA_BEGIN_PRE ( 1 << 7 )
+#define RBILA_END_PRE ( 1 << 8 )
+#define RBILA_BEGIN_SOURCE ( 1 << 9 )
+#define RBILA_END_SOURCE ( 1 << 10 )
+
+enum ItemLineKind
+{
+ ITEM_LINE_RAW, /* A line that does not start with a remark marker */
+ ITEM_LINE_PLAIN, /* A line that starts with a remark marker */
+ ITEM_LINE_PIPE, /* A line that starts with a remark marked and is
+ followed by a pipe marker. */
+ ITEM_LINE_END, /* The last line of an item */
+
+ ITEM_LINE_TOOL_START, // Start line of a tool item
+ ITEM_LINE_TOOL_BODY, // Body of a tool item
+ ITEM_LINE_TOOL_END, // End line of a tool item
+ ITEM_LINE_EXEC, // Exec item
+ ITEM_LINE_DOT_START, // Similar to TOOL_START but use DOT tool
+ ITEM_LINE_DOT_END, // End line of a DOT item
+ ITEM_LINE_DOT_FILE // DOT file to include
+};
+
+
+struct RB_Item_Line
+{
+ char *line;
+ enum ItemLineKind kind;
+ long format;
+ T_RB_DocType pipe_mode;
+};
+
+/****s* Items/RB_Item
+ * FUNCTION
+ * Keeps track of where items start end end in the header.
+ * The index numbers point to the lines array in
+ * RB_header.
+ * SOURCE
+ */
+
+struct RB_Item
+{
+ struct RB_Item *next;
+ enum ItemType type;
+ int no_lines;
+ struct RB_Item_Line **lines;
+ int begin_index;
+ int end_index;
+};
+
+/******/
+
+
+
+int RB_Get_Item_Type(
+ char * );
+int RB_Get_Item_Attr(
+ char *cmp_name );
+enum ItemType RB_Is_ItemName(
+ char *line );
+int RB_Ignore_Last_Item(
+ void );
+char *RB_Get_Item_Name(
+ void );
+struct RB_Item *RB_Create_Item(
+ enum ItemType arg_item_type );
+int Is_Ignore_Item(
+ char *name );
+int Works_Like_SourceItem(
+ enum ItemType item_type );
+
+int Is_Preformatted_Item(
+ enum ItemType item_type );
+
+int Is_Format_Item(
+ enum ItemType item_type );
+
+#endif /* ROBODOC_ITEMS_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/LaTeX_Generator
+ * FUNCTION
+ * Generator for LaTeX output. Supports singledoc mode.
+ *
+ *******
+ * TODO More documentation.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "generator.h"
+#include "util.h"
+#include "links.h"
+#include "latex_generator.h"
+#include "globals.h"
+#include "robodoc.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+static int verbatim = FALSE;
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_String
+ * FUNCTION
+ * Write a string to the destination document, escaping
+ * characters where necessary.
+ ******
+ */
+
+void RB_LaTeX_Generate_String(
+ FILE *dest_doc,
+ char *a_string )
+{
+ int i;
+ int l = strlen( a_string );
+ unsigned char c;
+
+ for ( i = 0; i < l; i++ )
+ {
+ c = a_string[i];
+ RB_LaTeX_Generate_Char( dest_doc, c );
+ }
+}
+
+
+void RB_LaTeX_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ RB_LaTeX_Generate_String( dest_doc, name );
+}
+
+void RB_LaTeX_Generate_Item_Begin(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+void RB_LaTeX_Generate_Item_End(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+/* void */
+/* RB_LaTeX_Generate_Item_Name( FILE* dest_doc, char *name ) */
+/* { */
+/* RB_LaTeX_Generate_String( dest_doc, name ); */
+/* fprintf( dest_doc, "\n" ); */
+/* } */
+
+
+/* Lowtexx 21.09.2005 11:02 */
+/* I think it looks better like this. This wastes less space in pdf-document. */
+void RB_LaTeX_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "\\textbf{" );
+ RB_LaTeX_Generate_String( dest_doc, name );
+
+ // In alternate format, item names stand alone (Thuffir)
+ if ( course_of_action.do_altlatex )
+ fprintf( dest_doc, "}\n" );
+ else
+ fprintf( dest_doc, ":}\\hspace{0.08in}" );
+}
+
+
+void RB_LaTeX_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header )
+{
+ int i;
+
+ // Generate a new page for every section in alternate format
+ if ( course_of_action.do_altlatex )
+ fprintf( dest_doc, "\\newpage\n" );
+
+ // If \part enabled, we have a level deeper
+ // So enable the level '0'
+ if ( course_of_action.do_latexparts )
+ depth--;
+
+ switch ( depth )
+ {
+ case 0:
+ fprintf( dest_doc, "\\part{" );
+ break;
+ case 1:
+ fprintf( dest_doc, "\\section{" );
+ break;
+ case 2:
+ fprintf( dest_doc, "\\subsection{" );
+ break;
+ case 3:
+ fprintf( dest_doc, "\\subsubsection{" );
+ break;
+ default:
+ /* Too deep so just make it a subsubsection */
+ fprintf( dest_doc, "\\subsubsection{" );
+ }
+
+ // Print Header "First" name
+ RB_LaTeX_Generate_String( dest_doc, name );
+ // Print other names
+ for ( i = 1; i < header->no_names; i++ )
+ {
+ RB_LaTeX_Generate_String( dest_doc, ", " );
+ RB_LaTeX_Generate_String( dest_doc, header->names[i] );
+ }
+ fprintf( dest_doc, "}\n" );
+
+ // Print Parent if any
+ if ( header->parent )
+ {
+ fprintf( dest_doc, "\\textsl{[ " );
+ RB_LaTeX_Generate_String( dest_doc, header->parent->function_name );
+ fprintf( dest_doc, " ]}\n" );
+ }
+
+ // Print Header Type
+ fprintf( dest_doc, "\\textsl{[ " );
+ RB_LaTeX_Generate_String( dest_doc, header->htype->indexName );
+ fprintf( dest_doc, " ]}\n\n" );
+}
+
+void RB_LaTeX_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( dest_doc );
+ USE( depth );
+ USE( name );
+ /* Empty */
+}
+
+char *RB_LaTeX_Get_Default_Extension(
+ void )
+{
+ return ".tex";
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Doc_Start
+ * NAME
+ * RB_LaTeX_Generate_Doc_Start --
+ *****
+ */
+
+void RB_LaTeX_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char *charset )
+{
+ fprintf( dest_doc, "%% Document: %s\n", name );
+ fprintf( dest_doc, "%% Source: %s\n", src_name );
+
+ if ( course_of_action.do_nogenwith )
+ {
+
+ }
+ else
+ {
+ fprintf( dest_doc, "%% " COMMENT_ROBODOC );
+/* Documentation is the users not ours fprintf( dest_doc, "%% " COMMENT_COPYRIGHT ); */
+ }
+
+ if ( course_of_action.do_headless )
+ {
+ /* The user wants a headless document, so we skip all
+ * initialization. It is up to the user to put this in.
+ */
+ }
+ else
+ {
+ fprintf( dest_doc, "\\documentclass{article}\n" );
+ fprintf( dest_doc, "\\usepackage{makeidx}\n" );
+ fprintf( dest_doc, "\\usepackage{graphicx}\n" );
+
+ // Check if we have to use the inputenc package
+ if ( charset )
+ {
+ fprintf( dest_doc, "\\usepackage[%s]{inputenc}\n", charset );
+ }
+
+ // Alternate mode, works better with pdflatex and DIN A4 sheets
+ // At least for me :) (Thuffir)
+ if ( course_of_action.do_altlatex )
+ {
+ fprintf( dest_doc, "\\oddsidemargin 0 cm\n" );
+ fprintf( dest_doc, "\\evensidemargin 0 cm\n" );
+ fprintf( dest_doc, "\\topmargin 0 cm\n" );
+ fprintf( dest_doc, "\\textwidth 16 cm\n" );
+ fprintf( dest_doc, "\\textheight 22 cm\n" );
+ }
+ else
+ {
+ /* I guess this is unecessarry (same definitions twice ?)
+ fprintf( dest_doc, "\\oddsidemargin 0.15 in\n" );
+ fprintf( dest_doc, "\\evensidemargin 0.35 in\n" );
+ fprintf( dest_doc, "\\marginparwidth 1 in \n" );
+ */
+
+ fprintf( dest_doc, "\\oddsidemargin 0.25 in \n" );
+ fprintf( dest_doc, "\\evensidemargin 0.25 in\n" );
+ fprintf( dest_doc, "\\marginparwidth 0.75 in\n" );
+ fprintf( dest_doc, "\\textwidth 5.875 in\n" );
+ }
+
+
+ fprintf( dest_doc, "\\setlength{\\parindent}{0in}\n" );
+ fprintf( dest_doc, "\\setlength{\\parskip}{.08in}\n\n" );
+
+ /* changed default header to use boldface (vs slant) */
+ fprintf( dest_doc, "\\pagestyle{headings}\n" );
+
+ // Set document title
+ fprintf( dest_doc, "\\title{%s}\n",
+ document_title ? document_title : DEFAULT_DOCTITILE );
+
+ if ( course_of_action.do_nogenwith )
+ {
+ fprintf( dest_doc, "\\author{Documentation Generator}\n" );
+ }
+ else
+ {
+ fprintf( dest_doc, "\\author{%s}\n", COMMENT_ROBODOC );
+ }
+
+ fprintf( dest_doc, "\\makeindex\n" );
+ fprintf( dest_doc, "\\begin{document}\n" );
+ fprintf( dest_doc, "\\maketitle\n" );
+
+ // In alternate mode, we generate INDEX at the end of document
+ if ( course_of_action.do_altlatex )
+ {
+ fprintf( dest_doc, "\\newpage\n" );
+ }
+ else
+ {
+ fprintf( dest_doc, "\\printindex\n" );
+ }
+
+ /* autogenerate table of contents! */
+ fprintf( dest_doc, "\\tableofcontents\n" );
+
+ // We don't need this in alternate format, since every section begins
+ // with a \newpage (Thuffir)
+ if ( !( course_of_action.do_altlatex ) )
+ fprintf( dest_doc, "\\newpage\n" );
+
+ /* trick to disable the autogenerated \newpage */
+ fprintf( dest_doc, "\n" );
+ }
+}
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Doc_End
+ * NAME
+ * RB_LaTeX_Generate_Doc_End --
+ *****
+ */
+
+void RB_LaTeX_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( name );
+
+ // In alternate mode, we generate INDEX at the end of document
+ if ( course_of_action.do_altlatex )
+ {
+ fprintf( dest_doc, "\\printindex\n" );
+ }
+
+ if ( course_of_action.do_footless )
+ {
+ /* The user does not want the foot of the
+ * document
+ */
+ }
+ else
+ {
+ fprintf( dest_doc, "\\end{document}\n" );
+ }
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Index_Entry
+ * FUNCTION
+ * Creates a entry for the index.
+ *
+ *******
+ */
+
+void RB_LaTeX_Generate_Index_Entry(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ assert( cur_header->function_name );
+
+ fprintf( dest_doc, "\\index{unsorted!" );
+ RB_LaTeX_Generate_String( dest_doc, cur_header->function_name );
+ if ( cur_header->is_internal )
+ {
+ fprintf( dest_doc, "}\\index{internal\\_%s!",
+ cur_header->htype->indexName );
+ }
+ else
+ {
+ fprintf( dest_doc, "}\\index{%s!", cur_header->htype->indexName );
+ }
+ RB_LaTeX_Generate_String( dest_doc, cur_header->function_name );
+ fprintf( dest_doc, "}\n" );
+}
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Header_Start
+ * NAME
+ * RB_LaTeX_Generate_Header_Start --
+ *****
+ */
+
+void RB_LaTeX_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ fprintf( dest_doc, "\\subsection{" );
+ RB_LaTeX_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "}\n" );
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Header_End
+ * NAME
+ * RB_LaTeX_Generate_Header_End --
+ *****
+ */
+
+void RB_LaTeX_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+ fputc( '\n', dest_doc );
+}
+
+
+/*x**f* LaTeX_Generator/RB_LaTeX_Generate_Index
+ * NAME
+ * RB_LaTeX_Generate_Index --
+ *****
+ */
+
+void RB_LaTeX_Generate_Index(
+ FILE *dest,
+ char *source )
+{
+ /* TODO REMOVE */
+ USE( dest );
+ USE( source );
+ assert( 0 );
+#if 0
+ RB_Generate_Doc_Start( dest, source, "Master File", 0 );
+ RB_Generate_LaTeX_Includes( dest );
+ RB_Generate_Doc_End( dest, source );
+#endif
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Index_Table
+ * NAME
+ * RB_LaTeX_Generate_Index_Table --
+ *****
+ */
+
+void RB_LaTeX_Generate_Index_Table(
+ FILE *dest,
+ int type,
+ char *title )
+{
+ USE( dest );
+ USE( type );
+ USE( title );
+
+ /* Empty */
+}
+
+/****f* LaTeX_Generator/Generate_LaTeX_Includes
+ * NAME
+ * Generate_LaTeX_Includes -- generate include commands
+ * SYNOPSIS
+ * void RB_Generate_LaTeX_Includes (FILE *dest)
+ * FUNCTION
+ * Generates a series of \include commands to include the
+ * documentation generated for each source file into one
+ * big file.
+ ****
+ */
+
+void RB_Generate_LaTeX_Includes(
+ FILE *dest )
+{
+ USE( dest );
+ /* TODO REMOVE ?? */
+#if 0
+ struct RB_link *cur_link;
+
+ for ( cur_link = first_link; cur_link; cur_link = cur_link->next_link )
+ {
+ {
+ if ( cur_link->type == NO_HEADER )
+ fprintf( dest, "\\include{%s}\n", cur_link->label_name );
+ }
+ }
+#endif
+}
+
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Empty_Item
+ * NAME
+ * RB_LaTeX_Generate_Empty_Item --
+ *****
+ */
+
+void RB_LaTeX_Generate_Empty_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\\\\\n" );
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_EscapedChar
+ * FUNCTION
+ * Generate a single character. These characters are outside
+ * a begin{verbatim} end{verbatim} block. So we need to escape is
+ * special characters.
+ * These are
+ * _ => \_
+ * % => \%
+ * $ => \$
+ * < => \textless
+ * > => \textgreater
+ * \ => $\backslash$
+ * SYNOPSIS
+ * void RB_LaTeX_Generate_EscapedChar( FILE* dest_doc, int c )
+ * SEE ALSO
+ * RB_LaTeX_Generate_Char()
+ *****
+ */
+
+void RB_LaTeX_Generate_EscapedChar(
+ FILE *dest_doc,
+ int c )
+{
+ switch ( c )
+ {
+ case '&':
+ case '~':
+ case '_':
+ case '%':
+ case '^':
+ case '{':
+ case '}':
+ case '$':
+ case '#':
+ fputc( '\\', dest_doc );
+ fputc( c, dest_doc );
+ break;
+/* lowtexx 21.09.2005 11:12 */
+/* Replace these characters by the correct latex-code */
+ case '\\':
+ fprintf( dest_doc, "$\\backslash$" );
+ break;
+ case '<':
+ fprintf( dest_doc, "\\textless " );
+ break;
+ case '>':
+ fprintf( dest_doc, "\\textgreater " );
+ break;
+/* --- */
+ default:
+ fputc( c, dest_doc );
+ break;
+ }
+}
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Char
+ * FUNCTION
+ * Generate a single character. These characters are generated
+ * within a begin{verbatim} end{verbatim} block So no escaping is
+ * necessary.
+ * SYNOPSIS
+ * void RB_LaTeX_Generate_Char( FILE* dest_doc, int c )
+ *****
+ */
+
+void RB_LaTeX_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ if ( verbatim )
+ {
+ switch ( c )
+ {
+ case '\n':
+ assert( 0 );
+ break;
+ case '\t':
+ assert( 0 );
+ break;
+ default:
+ fputc( c, dest_doc );
+ }
+ }
+ else
+ {
+ RB_LaTeX_Generate_EscapedChar( dest_doc, c );
+ }
+}
+
+
+void LaTeX_Generate_Begin_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\n" );
+}
+
+void LaTeX_Generate_End_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\n" );
+}
+
+
+void LaTeX_Generate_Begin_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\\begin{verbatim}\n" );
+ verbatim = TRUE;
+}
+
+void LaTeX_Generate_End_Preformatted(
+ FILE *dest_doc )
+{
+ verbatim = FALSE;
+ fprintf( dest_doc, "\\end{verbatim}\n" );
+}
+
+
+void LaTeX_Generate_Begin_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\\begin{itemize}\n" );
+}
+
+void LaTeX_Generate_End_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\\end{itemize}\n" );
+}
+
+void LaTeX_Generate_Begin_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, " \\item " );
+}
+
+void LaTeX_Generate_End_List_Item(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+// fprintf( dest_doc, "" );
+}
+
+
+
+/* lowtexx 21.09.2005 11:23 */
+/* added some functions to create links in latex documents */
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Label
+ * NAME
+ * RB_LaTeX_Generate_Label --
+ * SYNOPSIS
+ * void RB_LaTeX_Generate_Label( FILE* dest_doc, char* name)
+ * INPUTS
+ * dest_doc -- the file to which the text is written
+ * name -- the unique name of the label to create
+ * SOURCE
+ */
+
+void RB_LaTeX_Generate_Label(
+ FILE *dest_doc,
+ char *name )
+{
+ int i;
+ int l = strlen( name );
+ unsigned char c;
+
+ fprintf( dest_doc, "\\label{ch:" );
+ for ( i = 0; i < l; ++i )
+ {
+ c = name[i];
+ if ( utf8_isalnum( c ) )
+ {
+ RB_LaTeX_Generate_Char( dest_doc, c );
+ }
+ else
+ {
+ // think about this
+ // replaced by underscore
+ fputc( '_', dest_doc );
+ }
+ }
+ fprintf( dest_doc, "}\n" );
+}
+
+/******/
+
+
+/****f* LaTeX_Generator/RB_LaTeX_Generate_Link
+ * NAME
+ * RB_LaTeX_Generate_Link --
+ * SYNOPSIS
+ * void RB_LaTeX_Generate_Link( FILE *cur_doc, char *cur_name,
+ * char *filename, char *labelname,
+ * char *linkname )
+ * INPUTS
+ * cur_doc -- the file to which the text is written
+ * cur_name -- the name of the destination file (unused)
+ * (the file from which we link)
+ * filename -- the name of the file that contains the link
+ * (the file we link to) (unused)
+ * labelname-- the name of the unique label of the link.
+ * linkname -- the name of the link as shown to the user (unused).
+ * SOURCE
+ */
+
+void RB_LaTeX_Generate_Link(
+ FILE *cur_doc,
+ char *cur_name,
+ char *filename,
+ char *labelname,
+ char *linkname )
+{
+ USE( cur_name );
+ USE( filename );
+ USE( linkname );
+
+ // Only generate links outside the verbatim sections
+ // LaTeX does not seem to recognise them inside (Thuffir)
+ if ( verbatim == FALSE )
+ fprintf( cur_doc, " (\\ref{ch:%s})", labelname );
+}
+
+/******/
--- /dev/null
+#ifndef ROBODOC_LATEX_GENERATOR_H
+#define ROBODOC_LATEX_GENERATOR_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+
+void RB_LaTeX_Generate_Index_Entry(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_LaTeX_Generate_Item_Begin(
+ FILE *dest_doc );
+void RB_LaTeX_Generate_Item_End(
+ FILE *dest_doc );
+void RB_LaTeX_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name,
+ struct RB_header *header );
+void RB_LaTeX_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+
+/* size_t RB_LaTeX_Get_Len_Extension(); */
+/* void RB_LaTeX_Add_Extension(char* name); */
+char *RB_LaTeX_Get_Default_Extension(
+ void );
+void RB_LaTeX_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+void RB_LaTeX_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char *charset );
+void RB_LaTeX_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+void RB_LaTeX_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_LaTeX_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_LaTeX_Generate_Index(
+ FILE *dest,
+ char *source );
+void RB_Generate_LaTeX_Includes(
+ FILE *dest );
+void RB_LaTeX_Generate_Index_Table(
+ FILE *dest,
+ int type,
+ char *title );
+void RB_LaTeX_Generate_Item_Doc(
+ FILE *dest_doc,
+ char *dest_name,
+ char *begin_of_item,
+ char *end_of_item,
+ char *function_name,
+ int item_type );
+void RB_LaTeX_Generate_Empty_Item(
+ FILE *dest );
+void RB_LaTeX_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_LaTeX_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+
+void RB_LaTeX_Generate_String(
+ FILE *dest_doc,
+ char *a_string );
+void RB_LaTeX_Generate_EscapedChar(
+ FILE *dest_doc,
+ int c );
+
+void LaTeX_Generate_Begin_Paragraph(
+ FILE *dest_doc );
+void LaTeX_Generate_End_Paragraph(
+ FILE *dest_doc );
+void LaTeX_Generate_Begin_Preformatted(
+ FILE *dest_doc );
+void LaTeX_Generate_End_Preformatted(
+ FILE *dest_doc );
+void LaTeX_Generate_Begin_List(
+ FILE *dest_doc );
+void LaTeX_Generate_End_List(
+ FILE *dest_doc );
+void LaTeX_Generate_Begin_List_Item(
+ FILE *dest_doc );
+void LaTeX_Generate_End_List_Item(
+ FILE *dest_doc );
+
+void RB_LaTeX_Generate_Label(
+ FILE *dest_doc,
+ char *name );
+void RB_LaTeX_Generate_Link(
+ FILE *cur_doc,
+ char *cur_name,
+ char *filename,
+ char *labelname,
+ char *linkname );
+
+#endif /* ROBODOC_LATEX_GENERATOR_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Links
+ * FUNCTION
+ * This module contains functions to manipulate links.
+ * Links are derived from headers. They are used to create
+ * links in the documentation between a word and the part of
+ * the documentation that explains something about that word.
+ * (For instance a function name or variable name).
+ * In addition to the links derived from the headers links are
+ * also derived from the names of all the sourcefiles.
+ * MODIFICATION HISTORY
+ * ????-??-?? Frans Slothouber V1.0
+ * 2003-02-03 Frans Slothouber Refactoring
+ *******
+ * $Header: /cvsroot/robodoc/robo/Source/links.c,v 1.43 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "globals.h"
+#include "robodoc.h"
+#include "headers.h"
+#include "util.h"
+#include "links.h"
+#include "document.h"
+#include "part.h"
+#include "file.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+/* TODO Documentation */
+unsigned int link_index_size = 0;
+struct RB_link **link_index = NULL;
+struct RB_link **case_sensitive_link_index = NULL;
+
+
+/* Local functions */
+
+static struct RB_link *RB_Alloc_Link( char *label_name, char *object_name,
+ char *file_name );
+
+/* TODO Documentation */
+int link_cmp( void *l1, void *l2 )
+{
+ struct RB_link *link1 = l1;
+ struct RB_link *link2 = l2;
+
+ return RB_Str_Case_Cmp( link1->object_name, link2->object_name );
+}
+
+
+int case_sensitive_link_cmp( void *l1, void *l2 )
+{
+ struct RB_link *link1 = l1;
+ struct RB_link *link2 = l2;
+
+ return strcmp( link1->object_name, link2->object_name );
+}
+
+char * function_name( char * full_name )
+{
+ char * name = strchr( full_name, '/' );
+
+ if( name ) return name + 1;
+ else return full_name;
+}
+
+/****f* Links/RB_CollectLinks
+ * FUNCTION
+ * Convert header information into link information.
+ * RB_header -> RB_link conversion
+ * SYNOPSIS
+ */
+void
+RB_CollectLinks( struct RB_Document *document,
+ struct RB_header **headers,
+ unsigned long count )
+/*
+ * INPUTS
+ * * document --
+ * * headers -- the array with headers.
+ * * count -- number of headers in the array
+ * OUTPUT
+ * * link_index -- an array with links
+ * * link_index_size -- the number of links in the array.
+ * SOURCE
+ */
+
+{
+ unsigned long i, j;
+ int k;
+ struct RB_Part *i_part;
+
+ for ( i = j = 0; i < count; ++i )
+ {
+ j += headers[i]->no_names - 1;
+ }
+
+ link_index_size = count + j;
+
+ if ( ( document->actions.do_multidoc ) &&
+ ! ( document->actions.do_one_file_per_header )
+ )
+ {
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ if ( i_part->headers )
+ {
+ link_index_size++;
+ }
+ }
+ }
+
+ link_index =
+ ( struct RB_link ** ) calloc( link_index_size,
+ sizeof( struct RB_link ** ) );
+ case_sensitive_link_index =
+ ( struct RB_link ** ) calloc( link_index_size,
+ sizeof( struct RB_link ** ) );
+
+ for ( i = j = 0; i < count; ++i )
+ {
+ struct RB_link *link;
+ struct RB_header *header;
+
+ header = headers[i];
+ assert( header->unique_name );
+ assert( header->file_name );
+ for( k = 0; k < header->no_names; j++, k++ )
+ {
+ link = RB_Alloc_Link( header->unique_name, function_name(header->names[k]),
+ header->file_name );
+ link->htype = header->htype;
+ link->is_internal = header->is_internal;
+ link_index[j] = link;
+ case_sensitive_link_index[j] = link;
+ }
+ }
+
+ /* If we are in multidoc mode, we also create links
+ * for all the source files.
+ */
+
+ if ( ( document->actions.do_multidoc ) &&
+ /* but not for one file per header multidocs */
+ ! ( document->actions.do_one_file_per_header )
+ )
+ {
+ for ( i_part = document->parts; i_part; i_part = i_part->next )
+ {
+ if ( i_part->headers )
+ {
+ struct RB_link *link;
+
+ link =
+ RB_Alloc_Link( "robo_top_of_doc", i_part->filename->name,
+ RB_Get_FullDocname( i_part->filename ) );
+ i_part->filename->link = link;
+ link->htype = RB_FindHeaderType( HT_SOURCEHEADERTYPE );
+ link_index[j] = link;
+ case_sensitive_link_index[j] = link;
+ ++j;
+ }
+ else
+ {
+ i_part->filename->link = NULL;
+ }
+ }
+ }
+
+ /* Sort all the links so we can use a binary search */
+ RB_QuickSort( (void **)link_index, 0, link_index_size - 1, link_cmp );
+ RB_QuickSort( (void **)case_sensitive_link_index, 0, link_index_size - 1, case_sensitive_link_cmp );
+}
+
+/*****/
+
+
+/****f* Links/RB_Free_Links
+ * FUNCTION
+ * Deallocate all the memory used to store the links.
+ * SYNOPSIS
+ */
+void RB_Free_Links( void )
+/*
+ * INPUTS
+ * o link_index_size
+ * o link_index[]
+ * BUGS
+ * Should use RB_Free_Link instead of doing
+ * everything by it self.
+ * SOURCE
+ */
+{
+ struct RB_link *cur_link;
+ unsigned int i;
+
+ for ( i = 0; i < link_index_size; ++i )
+ {
+ cur_link = link_index[i];
+ free( cur_link->object_name );
+ free( cur_link->label_name );
+ free( cur_link->file_name );
+ free( cur_link );
+ }
+ free( link_index );
+}
+
+/*******/
+
+/* TODO Documentation */
+
+int RB_Number_Of_Links( struct RB_HeaderType* header_type, char* file_name, int internal )
+{
+ struct RB_link *cur_link;
+ int n = 0;
+ unsigned int i;
+
+ for ( i = 0; i < link_index_size; ++i )
+ {
+ cur_link = link_index[i];
+ if ( RB_CompareHeaderTypes( cur_link->htype, header_type ) &&
+ ( ( cur_link->is_internal && internal ) ||
+ ( !cur_link->is_internal && !internal ) ) )
+ {
+ if ( file_name )
+ {
+ if ( strcmp( file_name, cur_link->file_name ) == 0 )
+ {
+ n++;
+ }
+ }
+ else
+ {
+ n++;
+ }
+ }
+ }
+ return n;
+}
+
+/****f* Links/Find_Link [3.0h]
+ * NAME
+ * Find_Link -- try to match word with a link
+ * FUNCTION
+ * Searches for the given word in the list of links and
+ * headers. There are three passes (or four, when the C option
+ * is selected). Each pass uses a different definition of "word":
+ * o In the first pass it is any thing that ends with a 'space', a '.'
+ * or a ','.
+ * o In the second pass it is any string that consists of alpha
+ * numerics, '_', ':', '.', or '-'.
+ * o In the third pass (for C) it is any string that consists
+ * of alpha numerics or '_'.
+ * SYNOPSIS
+ */
+
+int
+Find_Link( char *word_begin,
+ char **object_name,
+ char **label_name,
+ char **file_name )
+/*
+ * INPUTS
+ * o word_begin - pointer to a word (a string).
+ * o object_name - pointer to a pointer to a string
+ * o file_name - pointer to a pointer to a string
+ * SIDE EFFECTS
+ * label_name & file_name are modified
+ * RESULT
+ * o object_name -- points to the object if a match was found,
+ * NULL otherwise.
+ * o file_name -- points to the file name if a match was found,
+ * NULL otherwise.
+ * o label_name -- points to the labelname if a match was found,
+ * o TRUE -- a match was found.
+ * o FALSE -- no match was found.
+ * NOTES
+ * This is a rather sensitive algorithm. Don't mess with it
+ * too much.
+ * SOURCE
+ */
+
+{
+ char *cur_char = NULL, old_char;
+ int low_index, high_index, cur_index, state, pass;
+ unsigned int length = 0;
+
+ for ( pass = 0; pass < 3; pass++ )
+ {
+ switch ( pass )
+ {
+ case 0:
+ {
+ for ( cur_char = word_begin;
+ ( *cur_char == '_' ) || utf8_isalnum( *cur_char ) || utf8_ispunct( *cur_char );
+ cur_char++ );
+ break;
+ }
+ case 1:
+ {
+ for ( cur_char = word_begin;
+ utf8_isalnum( *cur_char ) || ( *cur_char == '_' ) ||
+ ( *cur_char == '-' ) || ( *cur_char == '.' ) ||
+ ( *cur_char == ':' ); cur_char++ );
+ break;
+ }
+ case 2:
+ {
+ for ( cur_char = word_begin;
+ utf8_isalnum( *cur_char ) || ( *cur_char == '_');
+ cur_char++ );
+ break;
+ }
+ }
+
+ if ( ( ( *( cur_char - 1 ) ) == ',' ) || ( ( *( cur_char - 1 ) ) == '.' ) )
+ {
+ cur_char--;
+ }
+
+ old_char = *cur_char;
+ *cur_char = '\0'; /*
+ * End the word with a '\0'
+ */
+ if ( strlen( word_begin ) == length )
+ {
+ /* Do not test the same word over and over. If
+ * the current string and the string of the previous
+ * pass are the same length, they are the same. */
+
+ /* RB_Say ("Skipping (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin); */
+ }
+ else
+ {
+ length = strlen( word_begin );
+ /* RB_Say ("Testing (pass %d) \"%s\"\n", SAY_INFO, pass, word_begin); */
+ /*
+ * Search case sensitive for a link
+ */
+ for ( cur_index = 0, low_index = 0, high_index =
+ link_index_size - 1; high_index >= low_index; )
+ {
+ cur_index = ( high_index - low_index ) / 2 + low_index;
+ state = strcmp( word_begin, case_sensitive_link_index[cur_index]->object_name );
+ if ( state < 0 )
+ {
+ high_index = cur_index - 1;
+ }
+ else if ( state == 0 )
+ {
+ *object_name = case_sensitive_link_index[cur_index]->object_name;
+ *label_name = case_sensitive_link_index[cur_index]->label_name;
+ *file_name = case_sensitive_link_index[cur_index]->file_name;
+ RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
+ word_begin, *object_name, *file_name );
+ *cur_char = old_char;
+ return ( TRUE );
+ }
+ else if ( state > 0 )
+ {
+ low_index = cur_index + 1;
+ }
+ }
+
+ /*
+ * Search case insensitive for a link.
+ * But only when the user asks for this.
+ */
+ if ( course_of_action.do_ignore_case_when_linking )
+ {
+
+ for ( cur_index = 0, low_index = 0, high_index =
+ link_index_size - 1; high_index >= low_index; )
+ {
+ cur_index = ( high_index - low_index ) / 2 + low_index;
+ state = RB_Str_Case_Cmp( word_begin, link_index[cur_index]->object_name );
+ if ( state < 0 )
+ {
+ high_index = cur_index - 1;
+ }
+ else if ( state == 0 )
+ {
+ *object_name = link_index[cur_index]->object_name;
+ *label_name = link_index[cur_index]->label_name;
+ *file_name = link_index[cur_index]->file_name;
+ RB_Say( "linking \"%s\"->\"%s\" from \"%s\"\n", SAY_DEBUG,
+ word_begin, *object_name, *file_name );
+ *cur_char = old_char;
+ return ( TRUE );
+ }
+ else if ( state > 0 )
+ {
+ low_index = cur_index + 1;
+ }
+ }
+ }
+ }
+ *cur_char = old_char;
+ *file_name = NULL;
+ *object_name = NULL;
+ *label_name = NULL;
+ }
+ return ( FALSE );
+}
+
+/*****/
+
+
+/****f* Links/RB_Alloc_Link [2.01]
+ * NAME
+ * RB_Alloc_Link -- oop
+ * FUNCTION
+ * allocate struct + strings
+ * SYNOPSIS
+ */
+static struct RB_link *
+RB_Alloc_Link( char *label_name, char *object_name, char *file_name )
+/*
+ * INPUTS
+ * char *label_name -- strings to copy into the link
+ * char *file_name
+ * RESULT
+ * struct RB_link * -- ready-to-use
+ * AUTHOR
+ * Koessi
+ * SEE ALSO
+ * RB_StrDup(), RB_Free_Link()
+ * SOURCE
+ */
+
+{
+ struct RB_link *new_link;
+
+ assert( object_name );
+ assert( label_name );
+ assert( file_name );
+ RB_Say( "Allocating a link (%s %s %s)\n", SAY_DEBUG, object_name, label_name, file_name );
+ new_link = malloc( sizeof( struct RB_link ) );
+ memset( new_link, 0, sizeof( struct RB_link ) );
+
+ new_link->file_name = RB_StrDup( file_name );
+ new_link->object_name = RB_StrDup( object_name );
+ new_link->label_name = RB_StrDup( label_name );
+ return ( new_link );
+}
+
+/*****/
+
+/****f* Links/RB_Free_Link
+ * NAME
+ * RB_Free_Link -- oop
+ * FUNCTION
+ * free struct + strings
+ * SYNOPSIS
+ */
+void RB_Free_Link( struct RB_link *arg_link )
+/*
+ * INPUTS
+ * struct RB_link *link
+ * AUTHOR
+ * Koessi
+ * SEE ALSO
+ * RB_Alloc_Link(), RB_Close_The_Shop()
+ * SOURCE
+ */
+
+{
+ if ( arg_link )
+ {
+ if ( arg_link->label_name )
+ {
+ free( arg_link->label_name );
+ }
+ if ( arg_link->file_name )
+ {
+ free( arg_link->file_name );
+ }
+ free( arg_link );
+ }
+}
+
+/******/
+
--- /dev/null
+#ifndef ROBODOC_LINKS_H
+#define ROBODOC_LINKS_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headertypes.h"
+#include "headers.h"
+#include "document.h"
+
+/****s* Links/RB_link
+ * NAME
+ * RB_link -- link data structure
+ * PURPOSE
+ * Structure to store links to the documentation of an component.
+ * ATTRIBUTES
+ * * label_name -- the label under which the component can be found.
+ * this should be a unique name.
+ * * object_name -- the proper name of the object
+ * * file_name -- the file the component can be found in.
+ * * type -- the type of component (the header type).
+ * * is_internal -- is the header an internal header?
+ * SOURCE
+ */
+
+struct RB_link
+{
+ char *label_name;
+ char *object_name;
+ char *file_name;
+ struct RB_HeaderType *htype;
+ int is_internal;
+};
+
+/*********/
+
+int Find_Link(
+ char *word_begin,
+ char **object_name,
+ char **unique_name,
+ char **file_name );
+void RB_CollectLinks(
+ struct RB_Document *document,
+ struct RB_header **headers,
+ unsigned long count );
+void RB_Free_Links(
+ void );
+void RB_Free_Link(
+ struct RB_link *arg_link );
+
+int RB_Number_Of_Links(
+ struct RB_HeaderType *header_type,
+ char *file_name,
+ int internal );
+
+#endif /* ROBODOC_LINKS_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+#ifdef USE_LUA
+
+/****m* ROBODoc/Lua_Generator
+ * FUNCTION
+ * Shell generator that actually gives data to a Lua script.
+ * Supports singledoc mode.
+ *
+ * AUTHOR
+ * Jeremy Cowgar <jc@cowgar.com>
+ *
+ * NOTES
+ * No code defined yet. This is just a shell.
+ *******
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "generator.h"
+#include "util.h"
+#include "links.h"
+#include "lua_generator.h"
+#include "globals.h"
+#include "robodoc.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+/* Source to come soon */
+
+#endif
--- /dev/null
+#ifdef USE_LUA
+#ifndef ROBODOC_LUA_GENERATOR_H
+#define ROBODOC_LUA_GENERATOR_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+#endif /* ROBODOC_LUA_GENERATOR_H */
+#endif /* USE_LUA */
--- /dev/null
+# vi: ff=unix
+# $Id: makefile.am,v 1.24 2007/01/13 11:43:10 gumpu Exp $
+# Process this file with automake to produce Makefile.in
+
+bin_PROGRAMS = robodoc robohdrs
+
+robohdrs_SOURCES = \
+ robohdrs.c headers.c util.c globals.c roboconfig.c \
+ headertypes.c
+
+robohdrs_DEPENDENCIES = headers.h
+
+robodoc_SOURCES = \
+ optioncheck.c optioncheck.h \
+ analyser.c analyser.h \
+ config.h \
+ generator.c generator.h \
+ headers.c headers.h \
+ headertypes.c headertypes.h \
+ items.c items.h \
+ links.c links.h \
+ globals.c globals.h \
+ robodoc.c robodoc.h \
+ util.c util.h \
+ html_generator.c html_generator.h \
+ xmldocbook_generator.c xmldocbook_generator.h \
+ latex_generator.c latex_generator.h \
+ rtf_generator.c rtf_generator.h \
+ test_generator.c test_generator.h \
+ ascii_generator.c ascii_generator.h \
+ troff_generator.c troff_generator.h \
+ lua_generator.c lua_generator.h \
+ document.c document.h \
+ directory.c directory.h \
+ file.c file.h \
+ part.c part.h \
+ path.c path.h \
+ roboconfig.c roboconfig.h
--- /dev/null
+#****h* ROBODoc/Makefile.bcc
+# NAME
+# Makefile.bcc -- Makefile for Borland C++ Compiler 5.5
+# Adapted from Makefile.plain
+#
+# SYNOPSIS
+# make -f makefile.bcc robodoc
+# PURPOSE
+# The following targets are the most useful for the user.
+# robodoc - makes the robodc executable.
+#
+#****
+#
+# $Id: makefile.bcc,v 1.7 2007/05/04 13:31:31 petterik Exp $
+#
+
+SHELL = /bin/sh
+.SUFFIXES:
+.SUFFIXES: .c .obj
+
+#--------------------------------------
+
+CC = bcc32
+CFLAGS = -DRB_BCC -DIGNORE_CASE_FILENAMES
+LIBS=
+
+#
+#
+#
+
+BROWSER=netscape
+ROBODOC=./robodoc
+ETAGS=/usr/bin/etags
+EGREP=/bin/egrep
+RM=/bin/rm
+VERS=4.99.1
+RELEASE=1
+
+#--------------------------------------
+# sources for the robodoc executable
+#--------------------------------------
+
+# BCC -e filename.exe wouldn't work
+# so list robodoc.c first to ensure correct .exe name
+#
+
+SOURCES = analyser.c \
+ ascii_generator.c \
+ directory.c \
+ generator.c \
+ document.c \
+ globals.c \
+ headers.c \
+ headertypes.c \
+ html_generator.c \
+ items.c \
+ links.c \
+ file.c \
+ latex_generator.c \
+ part.c \
+ path.c \
+ roboconfig.c \
+ robodoc.c \
+ rtf_generator.c \
+ sgmldocbook_generator.c \
+ test_generator.c \
+ troff_generator.c \
+ util.c \
+ xmldocbook_generator.c
+
+HEADERS= analyser.h \
+ ascii_generator.h \
+ directory.h \
+ dirwalk.h \
+ document.h \
+ file.h \
+ generator.h \
+ globals.h \
+ headers.h \
+ headertypes.h \
+ html_generator.h \
+ items.h \
+ latex_generator.h \
+ links.h \
+ part.h \
+ path.h \
+ roboconfig.h \
+ robodoc.h \
+ rtf_generator.h \
+ sgmldocbook_generator.h \
+ test_generator.h \
+ troff_generator.h \
+ unittest.h \
+ util.h \
+ xmldocbook_generator.h
+
+OBJECTS= robodoc.obj \
+ analyser.obj \
+ ascii_generator.obj \
+ directory.obj \
+ generator.obj \
+ document.obj \
+ globals.obj \
+ headers.obj \
+ headertypes.obj \
+ html_generator.obj \
+ items.obj \
+ links.obj \
+ file.obj \
+ latex_generator.obj \
+ part.obj \
+ path.obj \
+ roboconfig.obj \
+ rtf_generator.obj \
+ sgmldocbook_generator.obj \
+ test_generator.obj \
+ troff_generator.obj \
+ util.obj \
+ xmldocbook_generator.obj
+
+#****e* Makefile.bcc/robodoc
+# NAME
+# robodoc --
+# NOTE
+# This assumes that you version of make knows how to make an .obj file
+# out of an .c file.
+# SOURCE
+#
+
+robodoc : $(OBJECTS)
+ $(CC) $(OBJECTS) $(LIBS)
+
+#****
+#------------------------------
+# Construction of the makefile
+#------------------------------
+
+depend :
+ makedepend -Y"" -f makefile.bcc $(SOURCES) $(HEADERS)
+
+depend2:
+ $(CC) -MM $(SOURCES)
+
+# DO NOT DELETE
+
+analyser.o : robodoc.h globals.h headers.h items.h util.h links.h \
+ analyser.h document.h file.h path.h part.h
+ascii_generator.o : ascii_generator.h headers.h util.h robodoc.h globals.h \
+ items.h generator.h document.h
+directory.o : robodoc.h directory.h file.h path.h
+generator.o : globals.h robodoc.h headers.h items.h util.h links.h \
+ generator.h document.h part.h file.h path.h roboconfig.h \
+ html_generator.h latex_generator.h sgmldocbook_generator.h \
+ rtf_generator.h troff_generator.h ascii_generator.h analyser.h
+document.o : robodoc.h document.h part.h path.h directory.h file.h \
+ headers.h links.h util.h generator.h
+globals.o : robodoc.h globals.h headers.h util.h
+headers.o : robodoc.h headers.h
+headertypes.o : robodoc.h headertypes.h
+html_generator.o : html_generator.h headers.h util.h globals.h robodoc.h \
+ links.h generator.h document.h items.h
+items.o : globals.h robodoc.h items.h roboconfig.h
+links.o : globals.h robodoc.h headers.h util.h links.h
+file.o : file.h path.h headers.h util.h
+latex_generator.o : generator.h robodoc.h headers.h document.h util.h \
+ links.h latex_generator.h globals.h
+part.o : headers.h file.h path.h part.h util.h
+path.o : path.h robodoc.h headers.h util.h
+roboconfig.o : util.h roboconfig.h
+robodoc.o : robodoc.h globals.h headers.h items.h util.h links.h \
+ part.h analyser.h document.h generator.h directory.h file.h path.h \
+ roboconfig.h
+rtf_generator.o : rtf_generator.h headers.h util.h robodoc.h globals.h \
+ generator.h document.h items.h
+sgmldocbook_generator.o : sgmldocbook_generator.h headers.h util.h \
+ robodoc.h globals.h links.h generator.h document.h items.h
+test_generator.o : test_generator.h globals.h util.h
+troff_generator.o : troff_generator.h headers.h util.h robodoc.h items.h \
+ generator.h document.h
+util.o : robodoc.h globals.h links.h headers.h items.h util.h
+xmldocbook_generator.o : xmldocbook_generator.h globals.h util.h
+
--- /dev/null
+#****h* ROBODoc/Makefile.mingw
+# NAME
+# Makefile.mingw -- Plain makefile that does not need autoconf
+# SYNOPSIS
+# make -f makefile.mingw robodoc
+# make -f makefile.mingw count
+# make -f makefile.mingw test
+# make -f makefile.mingw clean
+# PURPOSE
+# The makefile for MingW Minimalist GNU for Windows
+# See http://www.mingw.org
+#
+# You can use it if you are on a win32 system.
+#
+# The following targets are the most useful for the user.
+#
+# robodoc - makes the robodoc executable.
+#
+# Developers might try:
+# test -
+# count -
+# clean -
+#
+#****
+#
+# $Id: makefile.mingw,v 1.7 2007/05/14 15:41:19 petterik Exp $
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+#--------------------------------------
+# use gcc (generic)
+#--------------------------------------
+
+CC = gcc
+CFLAGS = -pedantic -O3 -Wall -W -std=c99 # -ansi
+LIBS=
+
+#
+#
+#
+
+ROBODOC=robodoc.exe
+RM=rm -f
+all: robodoc.exe
+
+# currently does not compile: robohdrs
+
+#--------------------------------------
+# sources for the robodoc executable
+#--------------------------------------
+
+SOURCES = analyser.c \
+ ascii_generator.c \
+ directory.c \
+ generator.c \
+ document.c \
+ globals.c \
+ headertypes.c \
+ headers.c \
+ html_generator.c \
+ items.c \
+ links.c \
+ file.c \
+ latex_generator.c \
+ part.c \
+ path.c \
+ roboconfig.c \
+ robodoc.c \
+ optioncheck.c \
+ rtf_generator.c \
+ sgmldocbook_generator.c \
+ troff_generator.c \
+ util.c \
+ test_generator.c \
+ xmldocbook_generator.c
+
+HEADERS= analyser.h \
+ ascii_generator.h \
+ directory.h \
+ dirwalk.h \
+ document.h \
+ file.h \
+ generator.h \
+ globals.h \
+ headertypes.h \
+ headers.h \
+ html_generator.h \
+ items.h \
+ latex_generator.h \
+ links.h \
+ optioncheck.h \
+ part.h \
+ path.h \
+ roboconfig.h \
+ robodoc.h \
+ rtf_generator.h \
+ sgmldocbook_generator.h \
+ troff_generator.h \
+ unittest.h \
+ util.h \
+ test_generator.h \
+ xmldocbook_generator.h
+
+OBJECTS= $(SOURCES:.c=.o)
+
+#****e* Makefile.mingw/robodoc
+# NAME
+# robodoc --
+# NOTE
+# This assumes that you version of make knows how to make an .o file
+# out of an .c file.
+# SOURCE
+#
+
+$(ROBODOC) : $(OBJECTS)
+ $(CC) $(OBJECTS) -o robodoc $(LIBS)
+
+#****
+
+#****e* Makefile.mingw/robohdrs
+# NAME
+# robohdrs --
+# SOURCE
+#
+
+robohdrs : robohdrs.o headers.o
+ $(CC) robohdrs.o headers.o -o robohdrs
+
+#****
+
+html : robodoc.html
+
+robodoc.html : $(ROBODOC)
+ ./$(ROBODOC) --src ./ --doc robodoc --singledoc --html --sections --toc
+
+#----------------------------
+# Development
+#----------------------------
+
+
+#****e* Makefile.mingw/test
+# NAME
+# test -- run some tests
+# FUNCTION
+# Runs robodoc on file with a number of different headers.
+# HISTORY
+# 2002-05-19/PetteriK: test cases in Test directory run with this rule
+#
+#****
+
+test : $(ROBODOC)
+ (cd Test; $(MAKE))
+
+#****e* Makefile.mingw/clean
+# NAME
+# clean -- Clean up the mess we made.
+# FUNCTION
+# Cleans up the mess we made.
+#*****
+
+clean :
+ $(RM) $(OBJECTS)
+ $(RM) robodoc
+ $(RM) robohdrs
+ $(RM) *~
+ $(RM) *.o
+ $(RM) *.tex
+ $(RM) *.toc
+ $(RM) *.dvi
+ $(RM) *.aux
+ $(RM) *.log
+ $(RM) *.ps
+ $(RM) robodoc.html
+ $(RM) testheaders.html
+ $(RM) stamp-h*
+ $(RM) makefile.in
+ $(RM) config.h
+
+
+#------------------------------
+# Construction of the makefile
+#------------------------------
+
+depend :
+ makedepend -Y"" -f makefile.mingw $(SOURCES) $(HEADERS)
+
+depend2:
+ $(CC) -MM $(SOURCES)
+
+# DO NOT DELETE
+
+analyser.o: robodoc.h globals.h headers.h headertypes.h items.h util.h
+analyser.o: links.h document.h analyser.h file.h path.h part.h
+ascii_generator.o: ascii_generator.h headers.h util.h robodoc.h globals.h
+ascii_generator.o: items.h generator.h document.h
+directory.o: robodoc.h directory.h file.h path.h links.h headertypes.h
+directory.o: headers.h document.h util.h globals.h roboconfig.h
+generator.o: globals.h robodoc.h headers.h items.h util.h links.h
+generator.o: headertypes.h document.h generator.h part.h file.h path.h
+generator.o: roboconfig.h html_generator.h latex_generator.h
+generator.o: sgmldocbook_generator.h xmldocbook_generator.h rtf_generator.h
+generator.o: troff_generator.h ascii_generator.h test_generator.h analyser.h
+document.o: robodoc.h document.h part.h path.h directory.h file.h links.h
+document.o: headertypes.h headers.h util.h generator.h
+globals.o: robodoc.h globals.h links.h headertypes.h headers.h document.h
+headertypes.o: headertypes.h util.h headers.h
+headers.o: robodoc.h headers.h globals.h roboconfig.h util.h
+html_generator.o: html_generator.h headers.h items.h document.h robodoc.h
+html_generator.o: util.h globals.h links.h headertypes.h generator.h
+html_generator.o: directory.h file.h path.h part.h
+items.o: globals.h robodoc.h items.h roboconfig.h util.h headers.h
+links.o: globals.h robodoc.h headers.h util.h links.h headertypes.h
+links.o: document.h part.h file.h path.h
+file.o: file.h path.h links.h headertypes.h headers.h document.h robodoc.h
+file.o: util.h
+latex_generator.o: generator.h robodoc.h headers.h document.h util.h links.h
+latex_generator.o: headertypes.h latex_generator.h globals.h
+part.o: headers.h file.h path.h links.h headertypes.h document.h robodoc.h
+part.o: part.h util.h
+path.o: path.h robodoc.h headers.h util.h
+roboconfig.o: headertypes.h util.h headers.h roboconfig.h
+robodoc.o: robodoc.h globals.h headers.h util.h links.h headertypes.h
+robodoc.o: document.h part.h analyser.h generator.h directory.h file.h path.h
+robodoc.o: roboconfig.h
+rtf_generator.o: rtf_generator.h headers.h util.h robodoc.h globals.h
+rtf_generator.o: generator.h document.h
+sgmldocbook_generator.o: sgmldocbook_generator.h headers.h
+sgmldocbook_generator.o: xmldocbook_generator.h document.h robodoc.h util.h
+sgmldocbook_generator.o: globals.h links.h headertypes.h generator.h items.h
+troff_generator.o: troff_generator.h headers.h util.h robodoc.h items.h
+troff_generator.o: generator.h document.h
+util.o: robodoc.h globals.h links.h headertypes.h headers.h document.h util.h
+test_generator.o: test_generator.h headers.h document.h robodoc.h globals.h
+test_generator.o: util.h
+xmldocbook_generator.o: xmldocbook_generator.h headers.h document.h robodoc.h
+xmldocbook_generator.o: globals.h util.h
+analyser.o: document.h robodoc.h
+ascii_generator.o: headers.h
+directory.o: file.h path.h links.h headertypes.h headers.h document.h
+directory.o: robodoc.h
+document.o: robodoc.h
+file.o: path.h links.h headertypes.h headers.h document.h robodoc.h
+generator.o: robodoc.h headers.h document.h
+globals.o: robodoc.h
+html_generator.o: headers.h items.h document.h robodoc.h
+latex_generator.o: headers.h
+links.o: headertypes.h headers.h document.h robodoc.h
+rtf_generator.o: headers.h
+sgmldocbook_generator.o: headers.h
+troff_generator.o: headers.h
+util.o: headers.h
+test_generator.o: headers.h document.h robodoc.h
+xmldocbook_generator.o: headers.h document.h robodoc.h
--- /dev/null
+# vi: spell ff=unix
+#****h* ROBODoc/makefile.mingw-cygwin
+# NAME
+# makefile.mingw-cygwin -- Plain makefile that does not need autoconf
+# PURPOSE
+# The makefile for MingW Minimalist GNU for Windows under the Cygwin
+# environment, See:
+# * http://www.mingw.org,
+# * http://www.cygwin.com
+#
+# You can use it if you are on a win32 system.
+#
+# The following targets are the most useful for the user:
+# * robodoc - makes the robodoc executable.
+#
+# Developers might try:
+# * test - run system tests
+# * clean - clean all results.
+#
+# EXAMPLES
+# make -f makefile.mingw-cygwin robodoc
+# make -f makefile.mingw-cygwin test
+# make -f makefile.mingw-cygwin clean
+#
+#****
+#
+# $Id: makefile.mingw-cygwin,v 1.17 2007/05/14 15:41:19 petterik Exp $
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+#--------------------------------------
+# use gcc (generic)
+#--------------------------------------
+
+CC = gcc
+#DEBUG = 1
+
+#****v* makefile.mingw-cygwin/CFLAGS
+# FUNCTION
+# Defined the flags used for the C compiler:
+# * -W, -Wall and -std=gnu99 --
+# This turns on all warnings based on the C99 standard.
+# Making the source warning free for an earlier standard is
+# not necessary as this code is compiled on Unix, Windows, and
+# Mac systems that all have C99 compliant C compilers.
+# It also allows some gnu extensions.
+# Using -std=c99 gives some spurious warnings about popen()
+# * -O3 -- optimize, turning optimization on finds more errors and
+# warnings.
+# * -mno-cygwin -- Tells gcc not to link with cygwin1.dll but use the
+# windows standard C library. (make mingw windows native executable
+# under cygwin)
+# * -s -- Strip debug info out from objects, making the executable smaller
+# * -g -- Include all debugger info for GDB
+# SOURCE
+
+ifdef DEBUG
+CFLAGS = -g -mno-cygwin -W -Wall -std=gnu99
+else
+CFLAGS = -s -mno-cygwin -O3 -W -Wall -std=gnu99
+endif
+
+#*****
+
+LIBS=
+
+#
+#
+#
+
+ROBODOC=robodoc.exe
+RM=rm -f
+all: robodoc.exe
+
+# currently does not compile: robohdrs
+
+#****v* makefile.mingw-cygwin/SOURCES, HEADERS
+# FUNCTION
+# All source files needed to compile ROBODoc
+# SOURCE
+#
+SOURCES = \
+ analyser.c \
+ ascii_generator.c \
+ directory.c \
+ document.c \
+ file.c \
+ generator.c \
+ globals.c \
+ headers.c \
+ headertypes.c \
+ html_generator.c \
+ items.c \
+ latex_generator.c \
+ links.c \
+ optioncheck.c \
+ part.c \
+ path.c \
+ roboconfig.c \
+ robodoc.c \
+ rtf_generator.c \
+ test_generator.c \
+ troff_generator.c \
+ util.c \
+ xmldocbook_generator.c
+
+HEADERS= analyser.h \
+ ascii_generator.h \
+ directory.h \
+ dirwalk.h \
+ document.h \
+ file.h \
+ generator.h \
+ globals.h \
+ headertypes.h \
+ headers.h \
+ html_generator.h \
+ items.h \
+ latex_generator.h \
+ links.h \
+ optioncheck.h \
+ part.h \
+ path.h \
+ roboconfig.h \
+ robodoc.h \
+ rtf_generator.h \
+ troff_generator.h \
+ unittest.h \
+ util.h \
+ test_generator.h \
+ xmldocbook_generator.h
+
+#*****
+
+OBJECTS= $(SOURCES:.c=.o)
+
+#****e* makefile.mingw-cygwin/robodoc
+# NAME
+# robodoc --
+# NOTE
+# This assumes that your version of make knows how to make an .o file
+# out of an .c file.
+# SOURCE
+#
+
+$(ROBODOC) : $(OBJECTS)
+ $(CC) $(CFLAGS) $(OBJECTS) -o $(ROBODOC) $(LIBS)
+
+#****
+
+#****e* makefile.mingw-cygwin/robohdrs
+# NAME
+# robohdrs --
+# SOURCE
+#
+
+robohdrs : robohdrs.o headers.o
+ $(CC) robohdrs.o headers.o -o robohdrs
+
+#****
+
+html : robodoc.html
+
+robodoc.html : $(ROBODOC)
+ ./$(ROBODOC) --src ./ --doc robodoc --singledoc --html --sections --toc
+
+#----------------------------
+# Development
+#----------------------------
+
+
+#****e* makefile.mingw-cygwin/test
+# NAME
+# test -- run some tests
+# FUNCTION
+# Runs robodoc on file with a number of different headers.
+# HISTORY
+# 2002-05-19/PetteriK: test cases in Test directory run with this rule
+#
+#****
+
+test : $(ROBODOC)
+ (cd Test; $(MAKE))
+
+#****e* makefile.mingw-cygwin/clean
+# NAME
+# clean -- Clean up the mess we made.
+# FUNCTION
+# Cleans up the mess we made.
+#*****
+
+clean :
+ $(RM) \
+ $(OBJECTS) \
+ $(ROBODOC) \
+ robohdrs \
+ *~ \
+ *.o \
+ *.tex \
+ *.toc \
+ *.dvi \
+ *.aux \
+ *.log \
+ *.ps \
+ robodoc.html \
+ testheaders.html \
+ stamp-h* \
+ makefile.in \
+ config.h \
+
+
+#------------------------------
+# Construction of the makefile
+#------------------------------
+
+# TODO Figure out how to get rid of all the warnings about
+# standard includes such as <stdio.h> etc
+depend :
+ makedepend -Y"" -f makefile.mingw-cygwin $(SOURCES) $(HEADERS)
+
+depend2:
+ $(CC) -MM $(SOURCES)
+
+# DO NOT DELETE
+
+analyser.o: analyser.c robodoc.h globals.h headers.h headertypes.h \
+ items.h util.h document.h links.h analyser.h file.h path.h part.h \
+ roboconfig.h
+ascii_generator.o: ascii_generator.c ascii_generator.h headers.h util.h \
+ document.h robodoc.h globals.h items.h headertypes.h generator.h
+directory.o: directory.c robodoc.h directory.h file.h path.h links.h \
+ headertypes.h headers.h document.h util.h globals.h roboconfig.h
+document.o: document.c robodoc.h document.h part.h path.h directory.h \
+ file.h links.h headertypes.h headers.h util.h generator.h globals.h
+file.o: file.c file.h path.h links.h headertypes.h headers.h document.h \
+ robodoc.h util.h
+generator.o: generator.c globals.h robodoc.h headers.h items.h util.h \
+ document.h links.h headertypes.h generator.h part.h file.h path.h \
+ roboconfig.h html_generator.h latex_generator.h xmldocbook_generator.h \
+ rtf_generator.h troff_generator.h ascii_generator.h test_generator.h \
+ analyser.h
+globals.o: globals.c robodoc.h globals.h links.h headertypes.h headers.h \
+ document.h
+headers.o: headers.c robodoc.h headers.h globals.h roboconfig.h util.h \
+ document.h
+headertypes.o: headertypes.c headertypes.h util.h headers.h document.h \
+ robodoc.h
+html_generator.o: html_generator.c html_generator.h headers.h items.h \
+ robodoc.h document.h util.h globals.h links.h headertypes.h generator.h \
+ directory.h file.h path.h part.h roboconfig.h
+items.o: items.c globals.h robodoc.h items.h roboconfig.h util.h \
+ headers.h document.h
+latex_generator.o: latex_generator.c generator.h robodoc.h headers.h \
+ document.h util.h links.h headertypes.h latex_generator.h globals.h
+links.o: links.c globals.h robodoc.h headers.h util.h document.h links.h \
+ headertypes.h part.h file.h path.h
+optioncheck.o: optioncheck.c robodoc.h optioncheck.h roboconfig.h util.h \
+ headers.h document.h
+part.o: part.c headers.h file.h path.h links.h headertypes.h document.h \
+ robodoc.h part.h util.h
+path.o: path.c path.h robodoc.h headers.h util.h document.h
+roboconfig.o: roboconfig.c headertypes.h util.h headers.h document.h \
+ robodoc.h roboconfig.h globals.h optioncheck.h
+robodoc.o: robodoc.c robodoc.h globals.h headers.h util.h document.h \
+ links.h headertypes.h part.h analyser.h generator.h directory.h file.h \
+ path.h roboconfig.h optioncheck.h
+rtf_generator.o: rtf_generator.c rtf_generator.h headers.h util.h \
+ document.h robodoc.h globals.h generator.h
+test_generator.o: test_generator.c test_generator.h headers.h document.h \
+ robodoc.h globals.h util.h
+troff_generator.o: troff_generator.c troff_generator.h headers.h items.h \
+ robodoc.h util.h document.h generator.h file.h path.h links.h \
+ headertypes.h part.h
+util.o: util.c robodoc.h globals.h links.h headertypes.h headers.h \
+ document.h path.h util.h
+xmldocbook_generator.o: xmldocbook_generator.c xmldocbook_generator.h \
+ headers.h document.h robodoc.h globals.h util.h
--- /dev/null
+# -*-makefile-*-
+# vi: ff=unix
+#
+#****h* ROBODoc/Makefile.plain
+# NAME
+# Makefile.plain -- Plain makefile that does not need autoconf
+# SYNOPSIS
+# * make -f makefile.plain robodoc
+# * make -f makefile.plain html
+# * make -f makefile.plain count
+# * make -f makefile.plain test
+# * make -f makefile.plain clean
+# * make -f makefile.plain xcompile
+#
+# PURPOSE
+# The makefile GCC.
+# You can use it if you are on a non Unix system or a system
+# that does not support autoconfiguration.
+#
+# The following targets are the most useful for the user:
+# * robodoc - makes the robodc executable.
+# * example - makes robodoc and shows you the autodocs
+# generated from the ROBODoc source code
+# using browser.
+# * html - makes autodocs for robodoc in html format.
+#
+# To build robodoc use:
+# * make -f makefile.plain robodoc
+#
+# Developers might try:
+# * depend - create dependencies
+# * test -
+# * count -
+# * clean -
+#
+# NOTES
+# This documentation is not complete. It is just a test to see
+# how to best use ROBODoc with makefiles.
+#
+#****
+#
+# $Id: makefile.plain,v 1.63 2007/07/10 19:13:52 gumpu Exp $
+#
+
+SHELL = /bin/sh
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+#--------------------------------------
+# use gcc (generic)
+#--------------------------------------
+
+CC = gcc
+# CFLAGS = -g -Wall -Wshadow -Wbad-function-cast -Wconversion -Wredundant-decls
+# -Wunreachable-code
+# TODO try to get this to compile with -Wconversion
+
+CFLAGS = -g -Wall -Wshadow -Wbad-function-cast -Wredundant-decls
+LIBS=
+
+#
+
+BROWSER=netscape
+ROBODOC=./robodoc
+ETAGS=/usr/bin/etags
+EGREP=/bin/egrep
+RM=/bin/rm
+MINGW_CC=/usr/bin/i586-mingw32msvc-gcc
+VERS=4.99.36
+RELEASE=1
+all: robodoc robohdrs
+
+#--------------------------------------
+# sources for the robodoc executable
+#--------------------------------------
+
+SOURCES = \
+ analyser.c \
+ ascii_generator.c \
+ directory.c \
+ generator.c \
+ document.c \
+ globals.c \
+ headers.c \
+ headertypes.c \
+ html_generator.c \
+ items.c \
+ links.c \
+ file.c \
+ latex_generator.c \
+ optioncheck.c \
+ part.c \
+ path.c \
+ roboconfig.c \
+ robodoc.c \
+ rtf_generator.c \
+ test_generator.c \
+ troff_generator.c \
+ util.c \
+ xmldocbook_generator.c
+
+HEADERS= \
+ analyser.h \
+ ascii_generator.h \
+ directory.h \
+ dirwalk.h \
+ document.h \
+ file.h \
+ generator.h \
+ globals.h \
+ headers.h \
+ headertypes.h \
+ html_generator.h \
+ items.h \
+ latex_generator.h \
+ links.h \
+ optioncheck.h \
+ part.h \
+ path.h \
+ roboconfig.h \
+ robodoc.h \
+ rtf_generator.h \
+ test_generator.h \
+ troff_generator.h \
+ unittest.h \
+ util.h \
+ xmldocbook_generator.h
+
+OBJECTS = $(SOURCES:.c=.o)
+DEPENDS = $(OBJECTS:.o=.d)
+
+#****e* Makefile.plain/robodoc
+# NAME
+# robodoc --
+# NOTE
+# This assumes that you version of make knows how to make an .o file
+# out of an .c file.
+# SOURCE
+#
+
+robodoc : $(OBJECTS)
+ $(CC) $(OBJECTS) -o robodoc$(EXE) $(LIBS)
+
+#****
+
+#****e* Makefile.plain/robohdrs
+# NAME
+# robohdrs --
+# SOURCE
+#
+
+robohdrs : robodoc robohdrs.o headers.o
+ $(CC) $(CFLAGS) util.o globals.o robohdrs.o headers.o roboconfig.o headertypes.o -o robohdrs$(EXE)
+
+#****
+
+html : robodoc.html
+
+robodoc.html : robodoc
+ ./robodoc --src ./ --doc robodoc --singledoc --html --sections --toc
+
+#****e* Makefile.plain/example
+# NAME
+# example -- create and show autodocs extracted from ROBODoc source.
+# FUNCTION
+#
+#****
+
+example : html
+ $(BROWSER) robodoc.html
+
+#----------------------------
+# Development
+#----------------------------
+
+tags :
+ $(ETAGS) *.c *.h
+
+#****e* Makefile.plain/count
+# NAME
+# count -- count the number of lines of the ROBODoc source.
+#****
+
+count :
+ $(EGREP) -v -G "^ \*" *.c *.h | egrep -v -G "/\*" | wc
+
+
+#****e* Makefile.plain/test
+# NAME
+# test -- run some tests
+# FUNCTION
+# Runs robodoc on file with a number of different headers.
+# HISTORY
+# 2002-05-19/PetteriK: test cases in Test directory run with this rule
+#
+#****
+
+test : $(ROBODOC)
+ (cd Test; $(MAKE))
+
+#****e* Makefile.plain/clean
+# NAME
+# clean -- Clean up the mess we made.
+# FUNCTION
+# Cleans up the mess we made.
+#*****
+
+clean :
+ $(RM) -f $(DOCS) $(XREF)
+ $(RM) -f robodoc robohdrs
+ $(RM) -f *~
+ $(RM) -f *.o *.tex *.toc *.dvi *.aux *.log *.ps *.exe
+ $(RM) -f robodoc.html
+ $(RM) -f testheaders.html
+ $(RM) -f stamp-h* makefile.in config.h
+
+#****e* Makefile.plain/xcompiler-test
+# NAME
+# xcompiler-test
+# SYNOPSIS
+# make -f makefile.plain xcompiler-test
+# NOTE
+# Used by do.sh.
+# RESULT
+# Exit status 0 if cross-compiler is found.
+# SOURCE
+#
+
+xcompiler-test:
+ test -x $(MINGW_CC)
+ zip -h > /dev/null 2>&1
+
+#*****
+
+#****e* Makefile.plain/xcompile
+# NAME
+# xcompile
+# SYNOPSIS
+# make -f makefile.plain xcompile
+# make -f makefile.plain MINGW_CC=/path/to/mingwcc xcompile
+# NOTE
+# Cross-compile Wintel binary.
+# Consider `apt-get install mingw32' before running this.
+# RESULT
+# Excutable `robodoc.exe' is created.
+# SOURCE
+#
+
+xcompile:
+ $(MAKE) -f makefile.plain CC=$(MINGW_CC) EXE=.exe robodoc
+
+#***** end xcompile
+
+#------------------------------
+# Construction of the makefile
+#------------------------------
+
+-include $(DEPENDS)
+
+depend:
+ $(CC) -MMD -E $(SOURCES) > /dev/null
+
--- /dev/null
+#****h* ROBODoc/Makefile.win32
+# FUNCTION
+# A makefile for win32.
+# Well it is not really a proper makefile since the dependencies are
+# missing, but it will compile robodoc using VC++
+# USAGE
+# nmake -f makefile.win32
+# nmake -f makefile.win32 clean
+# NOTES
+# run vcvars32.bat (part of VC++)
+# before using this file.
+#*****
+
+.SUFFIXES :
+.SUFFIXES : .obj .c
+.c.obj:
+ $(CC) $(CFLAGS) -c $<
+
+CC = cl
+
+#****v* Makefile.win32/CFLAGS
+# FUNCTION
+# The flags feeded to the compiler to compile a .c file.
+#
+# -I. look for include files in the current directory
+# -Zi enable debugging information.
+# -W3 turns on warnings,
+# -D RB_MSVC defines the symbol RB_MSVC which is needed
+# to compile the OS dependend parts of robodoc.
+# SOURCE
+#
+
+CFLAGS = -c -I. -nologo -Zi -W3 -D RB_MSVC -D IGNORE_CASE_FILENAMES
+
+#******
+
+CLINK = link
+CLINKFLAGS = /nologo /debug
+
+#****v* Makefile.win32/SOURCES
+# FUNCTION
+# List of all the sources needed to compile ROBODoc.
+# SOURCE
+SOURCES=analyser.c generator.c items.c util.c folds.c headers.c \
+ headertypes.c links.c robodoc.c directory.c part.c file.c path.c \
+ html_generator.c latex_generator.c rtf_generator.c \
+ troff_generator.c sgmldocbook_generator.c ascii_generator.c \
+ globals.c document.c roboconfig.c xmldocbook_generator.c \
+ test_generator.c
+#*****
+
+OBJECTS=$(SOURCES:.c=.obj)
+
+
+#****e* Makefile.win32/robodoc.trg
+# FUNCTION
+# Compile the robodoc executable. This makefile is
+# missing a dependencies sections, so use this target
+# only once.
+# SOURCE
+#
+robodoc.trg : $(OBJECTS)
+ $(CLINK) $(CLINKFLAGS) $(OBJECTS) /out:robodoc.exe
+#******
+
+#****v* Makefile.win32/clean
+# FUNCTION
+# Delete all the files created in the build process.
+# SOURCE
+#
+clean:
+ del $(OBJECTS)
+ del robodoc.exe
+ del *.pdb *.ilk
+ del tags
+#*****
+
+
+frans: robodoc.trg
+ copy robodoc.exe c:\pws\bin
+
+LINTOPT = -ic:\pclint8\lnt -u std.lnt env-vc6.lnt -e715 +dRB_MSVC
+
+# LINTOPT = -ic:\pclint8\lnt -u std.lnt env-vc6.lnt -e715 -e613 -e550 -e740 -e732 -e713 -e737 -e818 -e830 -e641 +dRB_MSVC
+
+#****ie* Makefile.win32/lint
+# FUNCTION
+# Runs lint on all the robodoc sources.
+# SOURCE
+#
+lint:
+ -c:\pclint8\lint-nt $(LINTOPT) globals.c
+ -c:\pclint8\lint-nt $(LINTOPT) latex_generator.c
+ -c:\pclint8\lint-nt $(LINTOPT) rtf_generator.c
+ -c:\pclint8\lint-nt $(LINTOPT) document.c
+ -c:\pclint8\lint-nt $(LINTOPT) file.c
+ -c:\pclint8\lint-nt $(LINTOPT) headers.c
+ -c:\pclint8\lint-nt $(LINTOPT) directory.c
+ -c:\pclint8\lint-nt $(LINTOPT) headertypes.c
+ -c:\pclint8\lint-nt $(LINTOPT) util.c
+ -c:\pclint8\lint-nt $(LINTOPT) roboconfig.c
+ -c:\pclint8\lint-nt $(LINTOPT) robodoc.c
+ -c:\pclint8\lint-nt $(LINTOPT) generator.c
+ -c:\pclint8\lint-nt $(LINTOPT) html_generator.c
+#******
--- /dev/null
+# Obsolete
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/* $Id: optioncheck.c,v 1.19 2007/07/10 19:13:52 gumpu Exp $*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "robodoc.h"
+#include "optioncheck.h"
+#include "roboconfig.h"
+#include "util.h"
+
+
+char short_use[] =
+ "Usage:\n"
+ " robodoc --src <directory> --doc <directory> --multidoc [type] [options]\n"
+ " robodoc --src <directory> --doc <file> --singledoc [type] [options]\n"
+ " robodoc --src <file> --doc <file> --singlefile [type] [options]\n"
+ "\n" "Use robodoc --help for more information\n";
+
+
+void Print_Short_Use(
+ void )
+{
+ fprintf( stdout, "%s\n", short_use );
+}
+
+/****t* UserInterface/Option_Test_Kind
+ * FUNCTION
+ * Enumeration for the kind of tests that are carried out on the
+ * options that the user specifies.
+ * SOURCE
+ */
+typedef enum
+{
+ TEST_INVALID = 0,
+ TEST_MUTUAL_EXCLUDE = 1,
+ TEST_SHOULD_EXISTS, /* TODO Create this kind of test */
+ TEST_NO_EFFECT, /* TODO Create this kind of test */
+ TEST_COUNT, /* TODO Create this kind of test */
+ TEST_SPELLING /* TODO Create this kind of test */
+} Option_Test_Kind;
+
+/*****/
+
+typedef enum
+{
+ OPTION_FATAL = 1,
+ OPTION_WARNING
+} Option_Error_Severity;
+
+/****s* UserInterface/RB_Option_Name
+ * FUNCTION
+ * Element in a list of option names.
+ * Used in a RB_Option_Test to specify to what
+ * options a test applies.
+ * SOURCE
+ */
+
+struct RB_Option_Name
+{
+ /* linked list administration */
+ struct RB_Option_Name *next;
+ /* name of the option */
+ char *name;
+ /* use by the Count test */
+ int count;
+};
+
+/*****/
+
+/****s* UserInterface/RB_Option_Test
+ * FUNCTION
+ * A test specification for options. This
+ * stores information about the kind of test and
+ * the options it applies to, and the message that
+ * is given to the user.
+ * SOURCE
+ */
+
+struct RB_Option_Test
+{
+ /* tests are stored in a linked list */
+ struct RB_Option_Test *next;
+ /* the group of options the test applies to. */
+ struct RB_Option_Name *option_group;
+ /* the kind of test */
+ Option_Test_Kind kind;
+ /* More information for the user */
+ char *more_information; /* TODO Fill and use */
+
+ Option_Error_Severity severity;
+};
+
+/******/
+
+/****v* UserInterface/ok_options
+ * FUNCTION
+ * An list of all allowed command-line options. If you add any
+ * options add its name here too. This list is used to verify
+ * the options specified by the user.
+ * SOURCE
+ */
+
+static char *ok_options[] = {
+ "--rc",
+ "--src",
+ "--doc",
+ "--html",
+ "--latex",
+ "--ascii",
+ "--rtf",
+ "--troff",
+ "--dbxml",
+ "--doctype_name",
+ "--doctype_location",
+ "--singledoc",
+ "--singlefile",
+ "--multidoc",
+ "--one_file_per_header",
+ "--first_section_level",
+ "--sections",
+ "--internal",
+ "--internalonly",
+ "--ignore_case_when_linking",
+ "--toc",
+ "--index",
+ "--nosource",
+ "--tabsize",
+ "--tell",
+ "--debug",
+ "--test", /* Special output mode for testing */
+ "--nodesc",
+ "--nopre",
+ "--nogeneratedwith",
+ "--no_subdirectories",
+ "--cmode",
+ "--charset",
+ "--ext",
+ "--help",
+ "--css",
+ "--version",
+ "-c",
+ "--lock",
+ "--nosort",
+ "--headless",
+ "--footless",
+ "--sectionnameonly",
+ "--compress",
+ "--mansection",
+ "--verbal", /*FS TODO */
+ "--ms_errors", /* TODO is this documented?? */
+ "--documenttitle",
+ "--altlatex",
+ "--latexparts",
+ "--tabstops",
+ "--syntaxcolors",
+ "--syntaxcolors_enable",
+ "--dotname",
+ "--masterindex",
+ "--sourceindex",
+ "--header_breaks",
+ ( char * ) NULL
+};
+
+/*****/
+
+
+/****v* UserInterface/option_tests
+ * FUNCTION
+ * A linked lists of tests that check the options specified
+ * by the user.
+ * SOURCE
+ */
+static struct RB_Option_Test *option_tests = NULL;
+
+/*****/
+
+
+/****f* UserInterface/Add_Option_Test
+ * FUNCTION
+ * Add a test to the linked list of options tests.
+ * SYNOPSIS
+ */
+static void Add_Option_Test(
+ struct RB_Option_Test *option_test )
+/*
+ * INPUTS
+ * option_test -- the test to be added.
+ * SOURCE
+ */
+{
+ option_test->next = option_tests;
+ option_tests = option_test;
+}
+
+/*****/
+
+/****f* UserInterface/Add_Option_Name
+ * FUNCTION
+ * Add the name of an option to the group of option names an option
+ * test applies to.
+ * SYNOPSIS
+ */
+static void Add_Option_Name(
+ struct RB_Option_Test *option_test,
+ char *name )
+/*
+ * INPUTS
+ * option_test -- the option test
+ * name -- the name of the option
+ * SOURCE
+ */
+{
+ struct RB_Option_Name *new_option_name =
+ malloc( sizeof( struct RB_Option_Name ) );
+ new_option_name->name = RB_StrDup( name );
+ new_option_name->next = option_test->option_group;
+ new_option_name->count = 0;
+ option_test->option_group = new_option_name;
+}
+
+/****/
+
+/****f* UserInterface/Create_New_Option_Test
+ * FUNCTION
+ * Allocate and initialize a new option test.
+ * SYNOPSIS
+ */
+static struct RB_Option_Test *Create_New_Option_Test(
+ Option_Test_Kind kind,
+ Option_Error_Severity severity )
+/*
+ * INPUTS
+ * kind -- the kind of test that has to be created.
+ * SOURCE
+ */
+{
+ struct RB_Option_Test *new_option_test =
+ malloc( sizeof( struct RB_Option_Test ) );
+ new_option_test->next = NULL;
+ new_option_test->option_group = NULL;
+ new_option_test->kind = kind;
+ new_option_test->severity = severity;
+ return new_option_test;
+}
+
+/*****/
+
+/****f* UserInterface/Create_Test_Data
+ * FUNCTION
+ * Create a linked list of tests.
+ * SYNOPSIS
+ */
+static void Create_Test_Data(
+ void )
+/*
+ * TODO
+ * Generate this code automatically from a set
+ * of high-level specifications.
+ * SOURCE
+ */
+{
+ struct RB_Option_Test *cur_option_test = NULL;
+ int i;
+
+ cur_option_test =
+ Create_New_Option_Test( TEST_MUTUAL_EXCLUDE, OPTION_FATAL );
+ Add_Option_Name( cur_option_test, "--singledoc" );
+ Add_Option_Name( cur_option_test, "--multidoc" );
+ Add_Option_Name( cur_option_test, "--singlefile" );
+ Add_Option_Test( cur_option_test );
+
+ cur_option_test =
+ Create_New_Option_Test( TEST_MUTUAL_EXCLUDE, OPTION_FATAL );
+ Add_Option_Name( cur_option_test, "--html" );
+ Add_Option_Name( cur_option_test, "--rtf" );
+ Add_Option_Name( cur_option_test, "--ascii" );
+ Add_Option_Name( cur_option_test, "--dbxml" );
+ Add_Option_Name( cur_option_test, "--troff" );
+ Add_Option_Name( cur_option_test, "--latex" );
+ Add_Option_Name( cur_option_test, "--test" );
+ Add_Option_Test( cur_option_test );
+
+ /* Order is important here */
+ cur_option_test = Create_New_Option_Test( TEST_COUNT, OPTION_FATAL );
+ for ( i = 0; ok_options[i]; i++ )
+ {
+ Add_Option_Name( cur_option_test, ok_options[i] );
+ }
+ Add_Option_Test( cur_option_test );
+
+}
+
+/******/
+
+static int Do_Count_Test(
+ struct RB_Option_Test *cur_option_test )
+{
+ unsigned int parameter_nr = 0;
+ int result = EXIT_SUCCESS;
+ struct RB_Option_Name *option_name;
+
+ assert( cur_option_test );
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ for ( option_name = cur_option_test->option_group;
+ option_name; option_name = option_name->next )
+ {
+ if ( RB_Str_Case_Cmp
+ ( configuration.options.names[parameter_nr],
+ option_name->name ) == 0 )
+ {
+ ( option_name->count )++;
+ }
+ }
+ }
+
+ for ( option_name = cur_option_test->option_group;
+ option_name; option_name = option_name->next )
+ {
+ if ( option_name->count > 1 )
+ {
+ fprintf( stderr, "The option %s is used more than once.\n",
+ option_name->name );
+ result = EXIT_FAILURE;
+ }
+ }
+
+ return result;
+}
+
+
+/****f* UserInterface/Do_Mutual_Exlcude_Test
+ * FUNCTION
+ * Check all the options to see if combinations of options
+ * are used that mutually exclude each other, such as
+ * --singledoc and --multidoc.
+ * SYNOPSIS
+ */
+static int Do_Mutual_Exlcude_Test(
+ struct RB_Option_Test *cur_option_test )
+ /*
+ * INPUTS
+ * * cur_option_test -- the test to be carried out.
+ * SOURCE
+ */
+{
+ int n = 0;
+ unsigned int parameter_nr = 0;
+ int result = EXIT_SUCCESS;
+
+ assert( cur_option_test );
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ struct RB_Option_Name *option_name = cur_option_test->option_group;
+
+ for ( ; option_name; option_name = option_name->next )
+ {
+ if ( RB_Str_Case_Cmp
+ ( configuration.options.names[parameter_nr],
+ option_name->name ) == 0 )
+ {
+ ++n;
+ }
+ }
+ }
+
+ /* Only one of the options in the group may be used */
+ if ( n > 1 )
+ {
+ fprintf( stderr, "The options: " );
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ struct RB_Option_Name *option_name =
+ cur_option_test->option_group;
+ for ( ; option_name; option_name = option_name->next )
+ {
+ if ( RB_Str_Case_Cmp
+ ( configuration.options.names[parameter_nr],
+ option_name->name ) == 0 )
+ {
+
+ fprintf( stderr, "%s ",
+ configuration.options.names[parameter_nr] );
+ }
+ }
+ }
+ fprintf( stderr, "cannot be used together.\n" );
+ result = EXIT_FAILURE;
+ }
+
+ return result;
+}
+
+/*****/
+
+
+/****f* UserInterface/Do_Option_Tests
+ * FUNCTION
+ * Run a series of tests on the options that the user
+ * specified. These tests are specified in
+ * option_tests.
+ * SYNOPSIS
+ */
+static int Do_Option_Tests(
+ void )
+/*
+ * RESULT
+ * * EXIT_FAILURE -- one of the tests failed.
+ * * EXIT_SUCCESS -- all test completed successfully
+ * SOURCE
+ */
+{
+ struct RB_Option_Test *cur_option_test = NULL;
+ int result = EXIT_SUCCESS;
+ int final_result = EXIT_SUCCESS;
+
+ RB_Say( "Checking the option semantics.\n", SAY_INFO );
+ Create_Test_Data( );
+ cur_option_test = option_tests;
+
+ assert( cur_option_test );
+
+ for ( ; cur_option_test; cur_option_test = cur_option_test->next )
+ {
+ switch ( cur_option_test->kind )
+ {
+ case TEST_MUTUAL_EXCLUDE:
+ RB_Say( "Checking for mutual excluding options.\n", SAY_INFO );
+ result = Do_Mutual_Exlcude_Test( cur_option_test );
+ break;
+ case TEST_COUNT: /* TODO Create */
+ RB_Say( "Checking for duplicate options.\n", SAY_INFO );
+ result = Do_Count_Test( cur_option_test );
+ break;
+ case TEST_SHOULD_EXISTS: /* TODO Create */
+ case TEST_NO_EFFECT: /* TODO Create */
+ case TEST_SPELLING: /* TODO Create */
+ default:
+ assert( 0 );
+ }
+ /* If one of the tests fails the final result is a fail. */
+ if ( result == EXIT_FAILURE )
+ {
+ final_result = EXIT_FAILURE;
+ if ( cur_option_test->severity == OPTION_FATAL )
+ {
+ break;
+ }
+ }
+ }
+
+ return final_result;
+}
+
+/****/
+
+/****f* UserInterface/Check_Option_Spelling
+ * FUNCTION
+ * Check for misspelled options specified by the user.
+ * SYNOPSIS
+ */
+
+int Check_Option_Spelling(
+ void )
+/*
+ * RESULT
+ * * EXIT_SUCCESS -- all options are correctly spelled.
+ * * EXIT_FAILURE -- one of more options are misspelled.
+ * SOURCE
+ */
+{
+ char ok;
+ char *arg;
+ char **opts;
+ unsigned int parameter_nr;
+
+ RB_Say( "Checking the option syntax.\n", SAY_INFO );
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ arg = configuration.options.names[parameter_nr];
+ if ( ( arg[0] == '-' ) && ( arg[1] == '-' ) )
+ {
+ /* this arg is an option */
+ ok = 0;
+ opts = ok_options;
+ while ( *opts )
+ {
+ if ( strcmp( arg, *opts ) == 0 )
+ {
+ ok = 1;
+ break;
+ }
+ opts++;
+ }
+ if ( !ok )
+ {
+ fprintf( stderr, "Invalid argument: %s\n", arg );
+ fprintf( stderr,
+ "This might also be in your robodoc.rc file\n" );
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+/******/
+
+
+static int Check_Item_Names(
+ struct Parameters *arg_parameters,
+ char *block_name )
+{
+ unsigned int parameter_nr_1;
+ unsigned int parameter_nr_2;
+ int name_is_ok = TRUE;
+
+ RB_Say( "Checking the item names for %s block.\n", SAY_INFO, block_name );
+ for ( parameter_nr_1 = 0;
+ parameter_nr_1 < arg_parameters->number; parameter_nr_1++ )
+ {
+ name_is_ok = FALSE;
+ for ( parameter_nr_2 = 0;
+ parameter_nr_2 < configuration.items.number; parameter_nr_2++ )
+ {
+ if ( strcmp( configuration.items.names[parameter_nr_2],
+ arg_parameters->names[parameter_nr_1] ) == 0 )
+ {
+ name_is_ok = TRUE;
+ }
+ }
+ if ( !name_is_ok )
+ {
+ RB_Say( "!! block.\n", SAY_INFO );
+ fprintf( stderr, "Unknown item %s found in the\n",
+ arg_parameters->names[parameter_nr_1] );
+ fprintf( stderr, " %s block\nof your configuration file.\n",
+ block_name );
+ break;
+ }
+ }
+
+ RB_Say( "Is %d block.\n", SAY_INFO, name_is_ok );
+ return name_is_ok;
+}
+
+/****f* UserInterface/Check_Item_Options
+ * FUNCTION
+ * Check the validity of the item options. Users can specify their
+ * own items, item order, and items that ar to be ignored. This all
+ * should be consistent.
+ * SYNOPSIS
+ */
+static int Check_Item_Options(
+ void )
+/*
+ * RESULT
+ * * EXIT_SUCCESS -- all options are correct.
+ * * EXIT_FAILURE -- one of more options incorrect.
+ * SOURCE
+ */
+{
+ int item_OK = TRUE;
+ int result = TRUE;
+
+ RB_Say( "Checking the item names.\n", SAY_INFO );
+ result = item_OK
+ && Check_Item_Names( &( configuration.ignore_items ),
+ "ignore items:" );
+ item_OK = item_OK && result;
+ result =
+ Check_Item_Names( &( configuration.source_items ), "source items:" );
+ item_OK = item_OK && result;
+ result =
+ Check_Item_Names( &( configuration.preformatted_items ),
+ "preformatted items:" );
+ item_OK = item_OK && result;
+ result =
+ Check_Item_Names( &( configuration.format_items ), "format items:" );
+ item_OK = item_OK && result;
+ result = Check_Item_Names( &( configuration.item_order ), "item order:" );
+ item_OK = item_OK && result;
+ if ( item_OK )
+ {
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ return EXIT_FAILURE;
+ }
+}
+
+/*****/
+
+
+/****f* UserInterface/Check_Options
+ * FUNCTION
+ * Check the validity of all the options in configuration.options[].
+ * This runs a number of checks.
+ * SYNOPSIS
+ */
+
+int Check_Options(
+ void )
+/*
+ * RESULT
+ * * EXIT_SUCCESS -- all options are correct.
+ * * EXIT_FAILURE -- one of more options incorrect.
+ * SOURCE
+ */
+{
+ int status;
+
+ RB_Say( "Checking the options.\n", SAY_INFO );
+ status = Check_Option_Spelling( );
+ if ( status == EXIT_SUCCESS )
+ {
+ status = Do_Option_Tests( );
+ if ( status == EXIT_SUCCESS )
+ {
+ status = Check_Item_Options( );
+ }
+ else
+ {
+ Print_Short_Use( );
+ }
+ }
+ else
+ {
+ Print_Short_Use( );
+ }
+
+ /* TODO free option_tests */
+
+ return status;
+}
+
+/*****/
--- /dev/null
+#ifndef ROBODOC_OPTION_CHECK_H
+#define ROBODOC_OPTION_CHECK_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+int Check_Options(
+ void );
+
+void Print_Short_Use( void );
+
+#endif /* ROBODOC_OPTION_CHECK_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Part
+ * FUNCTION
+ * Structures and functions that deal with documentation parts. A
+ * part links a sourcefile to the documentation file and contains
+ * all the headers found in a sourcefile. Parts (in the form of
+ * struct RB_Part) are stored in a RB_Document structure.
+ *****
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "headers.h"
+#include "file.h"
+#include "part.h"
+#include "util.h"
+
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+/****f* Part/RB_Get_RB_Part
+ * FUNCTION
+ * Create a new RB_Part and initialize it.
+ * SYNOPSIS
+ */
+struct RB_Part* RB_Get_RB_Part( void )
+/*
+ * RESULT
+ * A freshly allocated and initializedand RB_Part.
+ * SOURCE
+ */
+{
+ struct RB_Part *part = NULL;
+ part = ( struct RB_Part * ) malloc( sizeof( struct RB_Part ) );
+ if ( part )
+ {
+ part->next = NULL;
+ part->filename = NULL;
+ part->headers = NULL;
+ part->last_header = NULL;
+ }
+ else
+ {
+ RB_Panic( "Out of memory! RB_Get_RB_Part()" );
+ }
+ return part;
+}
+
+/******/
+
+
+
+/****f* Part/RB_Free_RB_Part
+ * FUNCTION
+ * Free the memory used by an RB_Part. Most of this is handled in
+ * other functions.
+ * SYNOPSIS
+ */
+void RB_Free_RB_Part( struct RB_Part *part )
+/*
+ * INPUTS
+ * o part -- the part to be freed.
+ * SOURCE
+ */
+{
+ /* part->filename is freed by RB_Directory */
+ /* part->headers. Headers are freed by the document */
+ free( part );
+}
+
+/*******/
+
+/****f* Part/RB_Open_Source
+ * FUNCTION
+ * Open the sourcefile of this part.
+ * SYNOPSIS
+ */
+FILE* RB_Open_Source( struct RB_Part *part )
+/*
+ * INPUTS
+ * o part -- the part for which the file is opened.
+ * SOURCE
+ */
+{
+ char *sourcefilename = NULL;
+ FILE *result;
+
+ assert( part );
+ assert( part->filename );
+ sourcefilename = Get_Fullname( part->filename );
+ result = fopen( sourcefilename, "r" );
+ if ( result )
+ {
+ /* OK */
+ }
+ else
+ {
+ RB_Panic( "can't open %s!", sourcefilename );
+ }
+ return result;
+}
+
+/******/
+
+
+/* TODO Documentation */
+FILE* RB_Open_Documentation( struct RB_Part * part )
+{
+ char *docfilename = NULL;
+ FILE *result;
+
+ assert( part );
+ assert( part->filename );
+ docfilename = RB_Get_FullDocname( part->filename );
+ RB_Say( "Creating file %s\n", SAY_DEBUG, docfilename );
+ result = fopen( docfilename, "w" );
+ if ( result )
+ {
+ /* OK */
+ }
+ else
+ {
+ RB_Panic( "can't open %s!", docfilename );
+ }
+ return result;
+}
+
+
+/* TODO Documentation */
+void
+RB_Part_Add_Source( struct RB_Part *part, struct RB_Filename *sourcefilename )
+{
+ /* One sourcefile per part. */
+ part->filename = sourcefilename;
+}
+
+struct RB_Filename *RB_Part_Get_Source( struct RB_Part *part )
+{
+ return part->filename;
+}
+
+/* TODO Documentation */
+void
+RB_Part_Add_Header( struct RB_Part *part, struct RB_header *header )
+{
+ assert( header );
+ assert( header->module_name );
+ assert( header->function_name );
+
+ header->owner = part;
+ if ( part->last_header )
+ {
+ header->next = NULL;
+ part->last_header->next = header;
+ part->last_header = header;
+ }
+ else
+ {
+ header->next = NULL;
+ part->headers = header;
+ part->last_header = header;
+ }
+}
--- /dev/null
+#ifndef ROBODOC_PART_H
+#define ROBODOC_PART_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <stdio.h>
+
+/****s* Part/RB_Part
+ * NAME
+ * RB_Part -- a part of the total documentation
+ * FUNCTION
+ * RB_Parts are stored in RB_Document. For each source file there
+ * is an RB_Part. It points to the source file, the documentation
+ * file, and contains all the headers that were found in the source
+ * file.
+ * ATTRIBUTES
+ * o next -- pointer to the next part
+ * (to form a linked list).
+ * o filename -- Information over the path to the
+ * sourcefile and the correcsponding
+ * documentation file.
+ * o headers -- All the headers that were
+ * found in the sourcefile.
+ * o last_header -- pointer to the last element in the
+ * list of headers.
+ * This is used to make it possible
+ * to add the the heades in the
+ * same order as they were found in
+ * the source file.
+ *****
+ */
+
+struct RB_Part
+{
+ struct RB_Part *next;
+ struct RB_Filename *filename;
+ struct RB_header *headers;
+ struct RB_header *last_header;
+};
+
+
+struct RB_Part *RB_Get_RB_Part(
+ void );
+void RB_Free_RB_Part(
+ struct RB_Part *part );
+FILE *RB_Open_Documentation(
+ struct RB_Part *part );
+FILE *RB_Open_Source(
+ struct RB_Part *part );
+
+void RB_Part_Add_Header(
+ struct RB_Part *part,
+ struct RB_header *header );
+void RB_Part_Add_Source(
+ struct RB_Part *part,
+ struct RB_Filename *sourcefilename );
+void RB_Part_Add_Doc(
+ struct RB_Part *part,
+ struct RB_Filename *docfilename );
+void RB_Part_Dump(
+ struct RB_Part *part );
+struct RB_Filename *RB_Part_Get_Source(
+ struct RB_Part *part );
+
+#endif /* ROBODOC_PART_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "path.h"
+#include "robodoc.h"
+#include "headers.h"
+#include "util.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+/* TODO Documentation */
+
+struct RB_Path *
+RB_Get_RB_Path( char *arg_pathname )
+{
+ struct RB_Path *rb_path;
+ int needs_slash = FALSE;
+
+ if ( strlen( arg_pathname ) == 0 )
+ {
+ RB_Panic( "Trying to use a path with as name \"\"\n" );
+ return 0; /* Keep the compiler happy. */
+ }
+ else
+ {
+ /* Check if the path ends with a / if not we will need to add one. */
+ needs_slash = ( arg_pathname[strlen( arg_pathname ) - 1] != '/' );
+ rb_path = ( struct RB_Path * ) malloc( sizeof( struct RB_Path ) );
+
+ if ( ! rb_path )
+ {
+ RB_Panic( "Out of memory! %s()\n", "RB_Get_RB_Path" );
+ }
+
+ /* 2 extra for the '/' and '\0' */
+ rb_path->name =
+ ( char * ) calloc( strlen( arg_pathname ) + 2, sizeof( char ) );
+
+ if ( ! rb_path->name )
+ {
+ RB_Panic( "Out of memory! %s()\n", "RB_Get_RB_Path" );
+ }
+
+ *( rb_path->name ) = '\0';
+ rb_path->parent = NULL;
+ rb_path->next = NULL;
+ strcat( rb_path->name, arg_pathname );
+ if ( needs_slash )
+ {
+ strcat( rb_path->name, "/" );
+ }
+ rb_path->docname = NULL;
+ }
+ return rb_path;
+}
+
+/*x**f* ROBODoc/RB_Get_RB_Path2
+ * NAME
+ * RB_Get_RB_Path2 -- create a new RB_Path structure.
+ * FUNCTION
+ * NOTE
+ * Has a wrong name...
+ *****
+ */
+
+/* TODO Documentation */
+struct RB_Path *
+RB_Get_RB_Path2( char *arg_current_path, char *arg_subdirectory )
+{
+ struct RB_Path *rb_path;
+ rb_path = ( struct RB_Path * ) malloc( sizeof( struct RB_Path ) );
+ /* allocate memory for the path name,
+ it will consist of the current_pathname plus the
+ subdirectory plus a '\0' */
+ rb_path->name =
+ ( char * ) malloc( strlen( arg_current_path ) +
+ strlen( arg_subdirectory ) + 2 );
+
+ if ( ! rb_path->name )
+ {
+ RB_Panic( "Out of memory! %s()\n", "RB_Get_RB_Path2" );
+ }
+
+ strcpy( rb_path->name, arg_current_path );
+ strcat( rb_path->name, arg_subdirectory );
+ if ( arg_subdirectory[strlen( arg_subdirectory ) - 1] != '/' )
+ {
+ strcat( rb_path->name, "/" );
+ }
+ rb_path->docname = NULL;
+ rb_path->parent = NULL;
+ rb_path->next = 0;
+ return rb_path;
+}
+
+/*x**f* ROBODoc/RB_Free_RB_Path
+ * NAME
+ * RB_Free_RB_Path -- free a RB_Path structure.
+ *****
+ * TODO Documentation
+ */
+
+void
+RB_Free_RB_Path( struct RB_Path *arg_rb_path )
+{
+ free( arg_rb_path->name );
+ if ( arg_rb_path->docname )
+ {
+ free( arg_rb_path->docname );
+ }
+ free( arg_rb_path );
+}
+
--- /dev/null
+#ifndef ROBODOC_PATH_H
+#define ROBODOC_PATH_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****s* ROBODoc/RB_Path
+ * NAME
+ * RB_Path -- Path to a file
+ * ATTRIBUTES
+ * * next -- pointer to the next RB_Path structure.
+ * * parent -- the parent path (one directory up).
+ * * name -- null terminated string with the name of the path.
+ * (Path names can be relative)
+ * * docname -- the corresponding docpath.
+ * SOURCE
+ */
+
+struct RB_Path
+{
+ struct RB_Path *next;
+ struct RB_Path *parent;
+ char *name;
+ char *docname;
+};
+
+/*****/
+
+struct RB_Path *RB_Get_RB_Path(
+ char *arg_pathname );
+struct RB_Path *RB_Get_RB_Path2(
+ char *arg_current_path,
+ char *arg_subdirectory );
+void RB_Free_RB_Path(
+ struct RB_Path *arg_rb_path );
+
+#endif /* ROBODOC_PATH_H */
--- /dev/null
+// vi: spell ff=unix
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/Configuration
+ * FUNCTION
+ * Functions to access the ROBODoc configuration and configuration
+ * file (robodoc.rc) or the file specified with the --rc option.
+ *
+ * The robodoc.rc file consists of a number of blocks. Each
+ * block starts with a line of the form
+ *
+ * <block name>:
+ *
+ * This is followed by a number of lines of data. Each line starts
+ * with at least one space followed by the actual data.
+ *
+ * This module parses this data and stores it in the global
+ * configuration.
+ *
+ * NOTES
+ * Is missing a lot of documentation.
+ *
+ * You can not use RB_Say() in this module since the --tell flag
+ * won't be parsed until after this module has finished.
+ *
+ ******
+ * $Id: roboconfig.c,v 1.55 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "headertypes.h"
+#include "util.h"
+#include "roboconfig.h"
+#include "globals.h"
+#include "optioncheck.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+/****v* Configuration/default_item_names
+ * FUNCTION
+ * Defines the names of items that ROBODoc recognized as
+ * items by default if none are specified in the
+ * robodoc.rc file.
+ * SOURCE
+ */
+
+static char *default_item_names[] = {
+ "SOURCE", /* source code inclusion */
+ "NAME", /* Item name + short description */
+ "COPYRIGHT", /* who own the copyright : "(c) <year>-<year> by <company/person>" */
+ "SYNOPSIS", "USAGE", /* how to use it */
+ "FUNCTION", "DESCRIPTION", "PURPOSE", /* what does it */
+ "AUTHOR", /* who wrote it */
+ "CREATION DATE", /* when did the work start */
+ "MODIFICATION HISTORY", "HISTORY", /* who done what changes when */
+ "INPUTS", "ARGUMENTS", "OPTIONS", "PARAMETERS", "SWITCHES", /* what can we feed into it */
+ "OUTPUT", "SIDE EFFECTS", /* what output will be made */
+ "RESULT", "RETURN VALUE", /* what do we get returned */
+ "EXAMPLE", /* a clear example of the items use */
+ "NOTES", /* any annotations */
+ "DIAGNOSTICS", /* diagnostical output */
+ "WARNINGS", "ERRORS", /* warning & error-messages */
+ "BUGS", /* known bugs */
+ "TODO", "IDEAS", /* what to implement next & ideas */
+ "PORTABILITY", /* where does it come from, where will it work */
+ "SEE ALSO", /* references */
+ "METHODS", "NEW METHODS", /* oop methods */
+ "ATTRIBUTES", "NEW ATTRIBUTES", /* oop attributes */
+ "TAGS", /* tagitem description */
+ "COMMANDS", /* command description */
+ "DERIVED FROM", /* oop super class */
+ "DERIVED BY", /* oop sub class */
+ "USES", "CHILDREN", /* what modules are used by this one */
+ "USED BY", "PARENTS", /* which modules do use this */
+ NULL, /* don't delete, so we can count how many there are... */
+};
+
+/***********/
+
+static char *default_remark_begin_markers[] = {
+ "/*",
+ "(*",
+ "<!--",
+ "{*",
+ NULL
+};
+
+static char *default_remark_end_markers[] = {
+ "*/",
+ "*)",
+ "-->",
+ "*}",
+ NULL
+};
+
+/****v* Configuration/c_keywords
+ * FUNCTION
+ * The default C keywords.
+ * SOURCE
+ */
+
+static char *c_keywords[] = {
+
+ // ANSI C Keywords
+ "auto",
+ "break",
+ "case",
+ "char",
+ "const",
+ "continue",
+ "default",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "extern",
+ "float",
+ "for",
+ "goto",
+ "if",
+ "int",
+ "long",
+ "register",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "struct",
+ "switch",
+ "typedef",
+ "union",
+ "unsigned",
+ "void",
+ "volatile",
+ "while",
+
+ // Some preprocessor directives
+ "#include",
+ "#define",
+ "#undef",
+ "#if",
+ "#else",
+ "#elif",
+ "#endif",
+ "#ifdef",
+ "#ifndef",
+ "#pragma",
+
+ NULL, /* don't delete, so we can count how many there are... */
+};
+
+// C comments
+#define C_LINE_COMMENT "//"
+#define C_BLOCK_COMMENT_START "/*"
+#define C_BLOCK_COMMENT_END "*/"
+
+/***********/
+
+
+// Default header separation character
+static char *default_header_separate_chars[] = {
+ ",",
+ NULL
+};
+
+
+// Default header ignore character (to include remarks, version data, etc...)
+static char *default_header_ignore_chars[] = {
+ "[",
+ NULL
+};
+
+
+/* Maximum length of a line in the configuration file */
+#define BUFFER_LENGTH 2048
+
+
+/****v* Configuration/configuration
+ * FUNCTION
+ * This global stores all the configuration parameters specified on
+ * the command line and in the robodoc.rc file.
+ * SOURCE
+ */
+
+struct RB_Configuration configuration;
+
+/*****/
+
+
+static T_Block_Kind BlockKind(
+ char *line );
+static T_Line_Kind ConfigLineKind(
+ char *line );
+static void SecondScan(
+ FILE *f );
+static void Alloc_Parameters(
+ struct Parameters *parameters,
+ unsigned int size );
+static void AddParameter(
+ char *name,
+ struct Parameters *parameters );
+static void GetParameters(
+ char *line,
+ struct Parameters *parameters );
+static void Install_Custom_HeaderTypes(
+ void );
+static void Complement_Remark_Markers(
+ void );
+static void ComplementItemNames(
+ void );
+static void ComplementHeaderMarkers(
+ void );
+static char *Get_rc(
+ char *rcname );
+
+
+/****v* Configuration/keywords_hash_mask
+ * FUNCTION
+ * Mask for keyword hash function.
+ * This mask reduces the hash value for the actual hash table size.
+ * Also the size of the hash table can be derived from this mask:
+ * hash table size = keywords_hash_mask + 1
+ * SOURCE
+ */
+static unsigned int keywords_hash_mask;
+
+/*****/
+
+/****v* Configuration/keywords_hash
+ * FUNCTION
+ * This is the hash table for the keywords.
+ * See keywords_hash_s.
+ * SOURCE
+ */
+static struct keywords_hash_s **keywords_hash;
+
+/*****/
+
+/****f* Configuration/allocate_keywords_hash_table
+ * FUNCTION
+ * Allocates space for the keyword hash table.
+ *
+ * The size of the table depends on the number of keywords rounded up to the
+ * next power of two.
+ * SYNOPSIS
+ */
+void allocate_keywords_hash_table(
+ void )
+/*
+ * SOURCE
+ */
+{
+ unsigned int i;
+
+ // Calculate hash table size (powers of two)
+ for ( keywords_hash_mask = 2;
+ keywords_hash_mask < configuration.keywords.number;
+ keywords_hash_mask <<= 1 );
+ keywords_hash_mask -= 1;
+
+ // Allocate space for hash table
+ keywords_hash = RB_malloc( ( keywords_hash_mask + 1 ) *
+ sizeof( struct keywords_hash_s * ) );
+
+ // Zero out all rows
+ for ( i = 0; i <= keywords_hash_mask; i++ )
+ {
+ keywords_hash[i] = NULL;
+ }
+}
+
+/*****/
+
+/****f* Configuration/Hash_Keyword
+ * FUNCTION
+ * Calculate the hash value for a string
+ *
+ * The hash value is based on the CRC32 hash function. It is then reduced by
+ * an AND operation to the actual size of the hash table.
+ * SYNOPSIS
+ */
+unsigned long Hash_Keyword(
+ char *keyword,
+ unsigned int len )
+/*
+ * INPUTS
+ * - keyword -- The keyword string
+ * - len -- The length of the keyword string
+ * RETURN VALUE
+ * The hash value for the keyword.
+ * SOURCE
+ */
+{
+ // Generate hash value
+ return RB_crc32( ( unsigned char * ) keyword, len,
+ len ) & keywords_hash_mask;
+}
+
+/*****/
+
+
+/****f* Configuration/add_to_keywords_hash_table
+ * FUNCTION
+ * Add a keyword to the hash table
+ * SYNOPSIS
+ */
+void add_to_keywords_hash_table(
+ char *keyword )
+/*
+ * INPUTS
+ * keyword -- The keyword string
+ * SOURCE
+ */
+{
+ struct keywords_hash_s *tmp, **curr;
+ unsigned long hash;
+
+ // Allocate space for new entry in hash table
+ tmp = RB_malloc( sizeof( struct keywords_hash_s ) );
+ // and initialise it
+ tmp->keyword = keyword;
+ tmp->next = NULL;
+
+ // Calculate hash value
+ hash = Hash_Keyword( keyword, strlen( keyword ) );
+
+ // Seek to last element in hash table row
+ for ( curr = &( keywords_hash[hash] ); *curr;
+ curr = &( ( *curr )->next ) );
+
+ // Insert entry into hash table
+ *curr = tmp;
+}
+
+/*****/
+
+
+/****f* Configuration/Find_Keyword
+ * FUNCTION
+ * Find a keyword in the hash table
+ * SYNOPSIS
+ */
+char *Find_Keyword(
+ char *keyword,
+ int len )
+/*
+ * INPUTS
+ * - keyword -- The keyword string
+ * - len -- The length of the keyword string
+ * RETURN VALUE
+ * - pointer to the found keyword string in hash table or
+ * - NULL if the keyword is not found
+ * SOURCE
+ */
+{
+ unsigned long hash;
+ struct keywords_hash_s *curr;
+
+ // Calculate hash value
+ hash = Hash_Keyword( keyword, len );
+
+ // Seek through hash table row
+ for ( curr = keywords_hash[hash]; curr; curr = curr->next )
+ {
+ // Check for keyword in row element
+ if ( !strncmp( keyword, curr->keyword, len ) )
+ {
+ // Found it!
+ return curr->keyword;
+ }
+ }
+
+ // Keyword not found
+ return NULL;
+}
+
+/*****/
+
+
+/****f* Configuration/add_keywords_to_hash_table
+ * FUNCTION
+ * Initalize hash table and add all keywords from configuration.keywords
+ * to the hash table
+ * SOURCE
+ */
+void add_keywords_to_hash_table(
+ void )
+{
+ unsigned int i;
+
+ // If nothing to add, exit
+ if ( !configuration.keywords.number )
+ return;
+
+ // Make some allocations
+ Make_crc32_table( );
+ allocate_keywords_hash_table( );
+
+ // Add keywords to hash table
+ for ( i = 0; i < configuration.keywords.number; i++ )
+ {
+ add_to_keywords_hash_table( configuration.keywords.names[i] );
+ }
+}
+
+/*****/
+
+
+/* TODO Documentation */
+static void AllocOptions(
+ unsigned int argc,
+ char **argv )
+{
+ unsigned int i;
+
+ Alloc_Parameters( &( configuration.options ), argc );
+ for ( i = 0; i < argc; ++i )
+ {
+ AddParameter( argv[i], &( configuration.options ) );
+ }
+}
+
+
+/* TODO Documentation */
+
+char *Get_rc(
+ char *rcname )
+{
+ char *s = NULL;
+ char *s2 = NULL;
+ char *path = NULL;
+
+ if ( Stat_Path( 'e', rcname ) && Stat_Path( 'f', rcname ) )
+ {
+ return RB_StrDup( rcname );
+ }
+ else
+ {
+ if ( strchr( rcname, ':' ) || strchr( rcname, '/' ) )
+ {
+ /* The rc names is a proper path, and not just a filename,
+ * we stop searching */
+ }
+ else
+ {
+ s = getenv( "HOME" );
+ if ( !s )
+ {
+ if ( ( s = getenv( "HOMEDRIVE" ) )
+ && ( s2 = getenv( "HOMEPATH" ) ) )
+ {
+ /* HOMEDRIVE includes backslash */
+ path =
+ ( char * ) malloc( sizeof( char ) *
+ ( strlen( s ) + strlen( s2 ) + 1 +
+ 1 + strlen( rcname ) ) );
+ sprintf( path, "%s%s%c%s", s, s2, '\\', rcname );
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ path =
+ ( char * ) malloc( sizeof( char ) *
+ ( strlen( s ) + 1 + 1 +
+ strlen( rcname ) ) );
+ sprintf( path, "%s%c%s", s, '/', rcname );
+ }
+
+ if ( path && Stat_Path( 'e', path ) && Stat_Path( 'f', path ) )
+ {
+ return path;
+ }
+ else
+ {
+ char* sitespecific =
+#ifdef ROBO_PREFIX
+ ROBO_PREFIX
+ "/share/doc/robodoc/";
+#else
+ "/usr/local/robodoc/";
+#endif
+
+ if ( path )
+ {
+ free( path );
+ }
+
+ path =
+ ( char * ) malloc( sizeof( char ) *
+ ( strlen( sitespecific ) + 1 +
+ strlen( rcname ) ) );
+ sprintf( path, "%s%s", sitespecific, rcname );
+
+ /* default failed -- try site-specific config file */
+ if ( Stat_Path( 'e', path ) && Stat_Path( 'f', path ) )
+ {
+ /* site-specific file can be stat'ed */
+ return path;
+ }
+ else
+ {
+ free( path );
+ return NULL;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/****f* Configuration/ReadConfiguration
+ * FUNCTION
+ * Read the robodoc configuration file, and create
+ * a RB_Configuration structure.
+ * SYNOPSIS
+ */
+
+char *ReadConfiguration(
+ unsigned int argc,
+ char **argv,
+ char *filename )
+/*
+ * INPUTS
+ * o argc -- the arg count as received by main()
+ * o argv -- the arg valules as received by main()
+ * o filename -- an optional filename. If none is given,
+ * "robodoc.rc" is used.
+ * RESULT
+ * An initialized configuration (a global).
+ * SOURCE
+ */
+{
+ FILE *f = NULL;
+ char *path = NULL;
+
+ if ( filename )
+ {
+ path = Get_rc( filename );
+ if ( path )
+ {
+ f = fopen( path, "r" );
+ }
+ if ( !f )
+ {
+ /* It should open as the user claimed it exists somewhere */
+ RB_Panic( "Can't open %s\n", filename );
+ }
+ }
+ else
+ {
+ /* Try the default rc file */
+ path = Get_rc( "robodoc.rc" );
+ if ( path )
+ {
+ f = fopen( path, "r" );
+ }
+ }
+
+ AllocOptions( argc, argv );
+ Alloc_Parameters( &( configuration.items ), 10 );
+ Alloc_Parameters( &( configuration.ignore_items ), 10 );
+ Alloc_Parameters( &( configuration.source_items ), 10 );
+ Alloc_Parameters( &( configuration.preformatted_items ), 10 );
+ Alloc_Parameters( &( configuration.format_items ), 10 );
+ Alloc_Parameters( &( configuration.item_order ), 10 );
+
+ Alloc_Parameters( &( configuration.custom_headertypes ), 10 );
+ Alloc_Parameters( &( configuration.ignore_files ), 10 );
+ Alloc_Parameters( &( configuration.accept_files ), 10 );
+ Alloc_Parameters( &( configuration.header_markers ), 10 );
+ Alloc_Parameters( &( configuration.remark_markers ), 10 );
+ Alloc_Parameters( &( configuration.end_markers ), 10 );
+ Alloc_Parameters( &( configuration.remark_begin_markers ), 10 );
+ Alloc_Parameters( &( configuration.remark_end_markers ), 10 );
+ Alloc_Parameters( &( configuration.keywords ), 10 );
+ Alloc_Parameters( &( configuration.source_line_comments ), 10 );
+ Alloc_Parameters( &( configuration.header_ignore_chars ), 10 );
+ Alloc_Parameters( &( configuration.header_separate_chars ), 10 );
+
+ if ( f )
+ {
+ SecondScan( f );
+ fclose( f );
+ }
+ else
+ {
+ /* No .rc file found. That's OK */
+ }
+ ComplementItemNames( );
+ ComplementHeaderMarkers( );
+ Complement_Remark_Markers( );
+ Install_Custom_HeaderTypes( );
+
+ // Make keywords hash table (if necessarry)
+ add_keywords_to_hash_table( );
+
+ assert( configuration.items.number );
+
+ return path;
+}
+
+/******/
+
+
+/* TODO Documentation */
+static void Complement_Remark_Markers(
+ void )
+{
+ unsigned int i;
+
+ if ( configuration.remark_begin_markers.number )
+ {
+ /* The user specified his own remark_begin_markers */
+ }
+ else
+ {
+ for ( i = 0; default_remark_begin_markers[i]; ++i )
+ {
+ AddParameter( default_remark_begin_markers[i],
+ &( configuration.remark_begin_markers ) );
+ }
+ }
+
+ if ( configuration.remark_end_markers.number )
+ {
+ /* The user specified his own remark_end_markers */
+ }
+ else
+ {
+ for ( i = 0; default_remark_end_markers[i]; ++i )
+ {
+ AddParameter( default_remark_end_markers[i],
+ &( configuration.remark_end_markers ) );
+ }
+ }
+}
+
+
+/****f* Configuration/Find_Parameter_Exact
+ * FUNCTION
+ * Checks for the existence of a given configuration parameter
+ * (exact string match)
+ * SOURCE
+ */
+char *Find_Parameter_Exact(
+ struct Parameters *params,
+ char *paramname )
+{
+ unsigned int i;
+
+ // we are looking for an exact match
+ for ( i = 0; i < params->number; i++ )
+ {
+ if ( !strcmp( params->names[i], paramname ) )
+ {
+ // found it
+ return params->names[i];
+ }
+ }
+
+ // parameter not found
+ return NULL;
+}
+
+/******/
+
+/****f* Configuration/Find_Parameter_Partial
+ * FUNCTION
+ * Checks for the existence of a given configuration parameter
+ * (partial string match)
+ * SOURCE
+ */
+char *Find_Parameter_Partial(
+ struct Parameters *params,
+ char *paramname )
+{
+ unsigned int i;
+
+ // we are looking for a not exact match
+ for ( i = 0; i < params->number; i++ )
+ {
+ if ( !strncmp
+ ( params->names[i], paramname, strlen( params->names[i] ) ) )
+ {
+ // found it
+ return params->names[i];
+ }
+ }
+
+ // parameter not found
+ return NULL;
+}
+
+/******/
+
+
+/****f* Configuration/Find_Parameter_Char
+ * FUNCTION
+ * Checks for the existence of a given configuration parameter
+ * (Character match)
+ * SOURCE
+ */
+char *Find_Parameter_Char(
+ struct Parameters *params,
+ char param )
+{
+ unsigned int i;
+
+ for ( i = 0; i < params->number; i++ )
+ {
+ if ( params->names[i][0] == param )
+ {
+ // found it
+ return params->names[i];
+ }
+ }
+
+ // parameter not found
+ return NULL;
+}
+
+/******/
+
+
+/****f* Configuration/Install_C_Syntax
+ * FUNCTION
+ * Install default C keywords and comments
+ * SOURCE
+ */
+void Install_C_Syntax(
+ void )
+{
+ unsigned int i;
+
+ // Check if we can install our default C keywords
+ if ( !configuration.keywords.number )
+ {
+ for ( i = 0; c_keywords[i]; i++ )
+ {
+ AddParameter( c_keywords[i], &( configuration.keywords ) );
+ }
+
+ // Make keywords hash table (if necessarry)
+ add_keywords_to_hash_table( );
+ }
+
+ // Make sure that C line comment is present
+ if ( Find_Parameter_Exact
+ ( &( configuration.source_line_comments ), C_LINE_COMMENT ) == NULL )
+ {
+ AddParameter( C_LINE_COMMENT,
+ &( configuration.source_line_comments ) );
+ }
+
+
+ // Make sure that C block comment start is present
+ if ( Find_Parameter_Exact
+ ( &( configuration.remark_begin_markers ),
+ C_BLOCK_COMMENT_START ) == NULL )
+ {
+ AddParameter( C_BLOCK_COMMENT_START,
+ &( configuration.remark_begin_markers ) );
+ }
+
+ // Make sure that C block comment end is present
+ if ( Find_Parameter_Exact
+ ( &( configuration.remark_end_markers ),
+ C_BLOCK_COMMENT_END ) == NULL )
+ {
+ AddParameter( C_BLOCK_COMMENT_END,
+ &( configuration.remark_end_markers ) );
+ }
+}
+
+/******/
+
+
+
+
+/* TODO Documentation */
+static void ComplementHeaderMarkers(
+ void )
+{
+ unsigned int i;
+
+ if ( configuration.header_markers.number )
+ {
+ /* The user specified his own header_markers */
+ }
+ else
+ {
+ for ( i = 0; header_markers[i]; ++i )
+ {
+ AddParameter( header_markers[i],
+ &( configuration.header_markers ) );
+ }
+ }
+
+ if ( configuration.remark_markers.number )
+ {
+ /* The user specified his own remark_markers */
+ }
+ else
+ {
+ for ( i = 0; remark_markers[i]; ++i )
+ {
+ AddParameter( remark_markers[i],
+ &( configuration.remark_markers ) );
+ }
+ }
+
+ if ( configuration.end_markers.number )
+ {
+ /* The user specified his own end_markers */
+ }
+ else
+ {
+ for ( i = 0; end_markers[i]; ++i )
+ {
+ AddParameter( end_markers[i], &( configuration.end_markers ) );
+ }
+ }
+
+ if ( configuration.header_separate_chars.number )
+ {
+ /* The user specified his own header_separate_chars */
+ }
+ else
+ {
+ for ( i = 0; default_header_separate_chars[i]; ++i )
+ {
+ AddParameter( default_header_separate_chars[i],
+ &( configuration.header_separate_chars ) );
+ }
+ }
+
+ if ( configuration.header_ignore_chars.number )
+ {
+ /* The user specified his own header_ignore_chars */
+ }
+ else
+ {
+ for ( i = 0; default_header_ignore_chars[i]; ++i )
+ {
+ AddParameter( default_header_ignore_chars[i],
+ &( configuration.header_ignore_chars ) );
+ }
+ }
+}
+
+
+
+/****if* Config/ConfigLineKind
+ * FUNCTION
+ * Deterimine the kind of line we a currently processing.
+ * SYNOPSIS
+ */
+
+static T_Line_Kind ConfigLineKind(
+ char *line )
+/*
+ * INPUTS
+ * line -- the current line.
+ * RETURN
+ * The kind of line.
+ * SOURCE
+ */
+{
+ T_Line_Kind kind = CFL_UNKNOWN;
+
+ if ( *line == '\0' )
+ {
+ kind = CFL_EMPTYLINE;
+ }
+ else if ( *line == '#' )
+ {
+ kind = CFL_REMARK;
+ }
+ else if ( utf8_isspace( *line ) )
+ {
+ char *cur_char = line;
+
+ for ( ; *cur_char && utf8_isspace( *cur_char ); ++cur_char )
+ {
+ /* Empty */
+ }
+ if ( *cur_char == '\0' )
+ {
+ kind = CFL_EMPTYLINE;
+ }
+ else
+ {
+ /* There is atleast one non-space character */
+ kind = CFL_PARAMETER;
+ }
+ }
+ else
+ {
+ kind = CFL_SECTION;
+ }
+ return kind;
+}
+
+/********/
+
+
+static T_Block_Kind BlockKind(
+ char *line )
+{
+ T_Block_Kind section_kind = SK_UNKNOWN;
+
+ if ( strcmp( line, "items:" ) == 0 )
+ {
+ section_kind = SK_ITEMS;
+ }
+ else if ( strcmp( line, "options:" ) == 0 )
+ {
+ section_kind = SK_OPTIONS;
+ }
+ else if ( strcmp( line, "extensions:" ) == 0 )
+ {
+ printf
+ ( "Warning: the 'extensions:' block is obsolete, use 'ignore files:' instead\n" );
+ }
+ else if ( strcmp( line, "ignore items:" ) == 0 )
+ {
+ section_kind = SK_IGNOREITEMS;
+ }
+ else if ( strcmp( line, "source items:" ) == 0 )
+ {
+ section_kind = SK_SOURCE_ITEMS;
+ }
+ else if ( strcmp( line, "headertypes:" ) == 0 )
+ {
+ section_kind = SK_HEADERTYPES;
+ }
+ else if ( strcmp( line, "ignore files:" ) == 0 )
+ {
+ section_kind = SK_IGNORE_FILES;
+ }
+ else if ( strcmp( line, "accept files:" ) == 0 )
+ {
+ section_kind = SK_ACCEPT_FILES;
+ }
+ else if ( strcmp( line, "header markers:" ) == 0 )
+ {
+ section_kind = SK_HEADER_MARKERS;
+ }
+ else if ( strcmp( line, "remark markers:" ) == 0 )
+ {
+ section_kind = SK_REMARK_MARKERS;
+ }
+ else if ( strcmp( line, "end markers:" ) == 0 )
+ {
+ section_kind = SK_END_MARKERS;
+ }
+ else if ( strcmp( line, "remark begin markers:" ) == 0 )
+ {
+ section_kind = SK_REMARK_BEGIN_MARKERS;
+ }
+ else if ( strcmp( line, "remark end markers:" ) == 0 )
+ {
+ section_kind = SK_REMARK_END_MARKERS;
+ }
+ else if ( strcmp( line, "keywords:" ) == 0 )
+ {
+ section_kind = SK_KEYWORDS;
+ }
+ else if ( strcmp( line, "source line comments:" ) == 0 )
+ {
+ section_kind = SK_SOURCE_LINE_COMMENTS;
+ }
+ else if ( strcmp( line, "header ignore characters:" ) == 0 )
+ {
+ section_kind = SK_HEADER_IGNORE_CHARS;
+ }
+ else if ( strcmp( line, "header separate characters:" ) == 0 )
+ {
+ section_kind = SK_HEADER_SEPARATE_CHARS;
+ }
+ else if ( strcmp( line, "preformatted items:" ) == 0 )
+ {
+ section_kind = SK_PREFORMATTED_ITEMS;
+ }
+ else if ( strcmp( line, "format items:" ) == 0 )
+ {
+ section_kind = SK_FORMAT_ITEMS;
+ }
+ else if ( strcmp( line, "item order:" ) == 0 )
+ {
+ section_kind = SK_ITEM_ORDER;
+ }
+ else
+ {
+ RB_Panic( "unknown block kind \"%s\"\n", line );
+ }
+ return section_kind;
+}
+
+
+static void Install_Custom_HeaderTypes(
+ void )
+{
+ unsigned int i;
+ struct Parameters headertype;
+ unsigned int priority = 0;
+
+ // Install custom header types
+ for ( i = 0; i < configuration.custom_headertypes.number; i++ )
+ {
+ // Allocate some default space for parameters
+ Alloc_Parameters( &headertype, 10 );
+ // Break current line into parameters
+ GetParameters( configuration.custom_headertypes.names[i],
+ &headertype );
+
+ // Check how many parameters do we have
+ switch ( headertype.number )
+ {
+ // 3 parameters -> no priority specified, assign default
+ case 3:
+ priority = 0;
+ break;
+
+ // 4 parameters -> priority specified, convert it
+ case 4:
+ priority = atoi( headertype.names[3] );
+ break;
+
+ // Any more or less parameters are illegal
+ default:
+ RB_Panic( "Error near header type: '%s'\n"
+ "You must have either 3 or 4 parameters there !\n",
+ headertype.names[0] );
+ }
+
+ // Check if type character is legal
+ if ( strlen( headertype.names[0] ) > 1 )
+ {
+ RB_Panic( "Error near header type: '%s'\n"
+ "Type character can only be one character long !\n",
+ headertype.names[0] );
+ }
+
+ // Add custom header type
+ RB_AddHeaderType( headertype.names[0][0], headertype.names[1],
+ headertype.names[2], priority );
+
+ // Free temporary space
+ free( headertype.names );
+ }
+}
+
+
+
+static void Alloc_Parameters(
+ struct Parameters *parameters,
+ unsigned int size )
+{
+ parameters->size = size;
+ parameters->number = 0;
+ parameters->names = calloc( size, sizeof( char * ) );
+}
+
+
+/* TODO Documentation */
+static void AddParameter(
+ char *name,
+ struct Parameters *parameters )
+{
+ parameters->names[parameters->number] = RB_StrDup( name );
+ parameters->number++;
+ if ( parameters->number >= parameters->size )
+ {
+ parameters->size *= 2;
+ parameters->names =
+ realloc( parameters->names, parameters->size * sizeof( char * ) );
+ }
+}
+
+
+/****f* Configuration/GetParameters
+ * FUNCTION
+ * Parse a line of text and store the individual words in
+ * a Parameters structure. Words are seperated by spaces,
+ * the exception are words surrounded by quotes. So:
+ * aap noot mies "back to the future"
+ * contains four words.
+ * INPUTS
+ * o line -- the line of text.
+ * o parameters -- the set of parameters
+ * SOURCE
+ */
+
+static void GetParameters(
+ char *line,
+ struct Parameters *parameters )
+{
+ int i;
+ int n = strlen( line );
+
+ /* Remove any spaces at the end of the line */
+ for ( i = n - 1; i >= 0 && utf8_isspace( line[i] ); --i )
+ {
+ line[i] = '\0';
+ }
+
+ assert( i > 0 ); /* If i <= 0 then the line was empty
+ and that cannot be, because this
+ is supposed to be a parameter */
+
+ /* Skip any white space at the begin of the line. */
+ n = strlen( line );
+ for ( i = 0; i < n && utf8_isspace( line[i] ); ++i )
+ {
+ /* Empty */
+ }
+ line += i;
+
+ n = strlen( line );
+ for ( i = 0; i < n; /* empty */ )
+ {
+ char *name = line;
+
+ if ( line[i] == '"' )
+ {
+ /* It is quoted string, fetch everything until
+ * the next quote */
+ ++name; /* skip the double quote */
+ for ( ++i; ( i < n ) && ( line[i] != '"' ); ++i )
+ {
+ /* empty */
+ }
+ if ( i == n )
+ {
+ RB_Panic( "Missing quote in your .rc file in line:\n %s\n",
+ line );
+ }
+ else
+ {
+#if defined(__APPLE__)
+ /* hacked because of error when compiling on Mac OS X */
+ assert( line[i] == 34 );
+#else
+ assert( line[i] == '"' );
+#endif
+ line[i] = '\0';
+ AddParameter( name, parameters );
+ }
+ }
+ else
+ {
+ /* a single word, find the next space */
+ for ( ; ( i < n ) && !utf8_isspace( line[i] ); ++i )
+ {
+ /* empty */
+ }
+ if ( i < n )
+ {
+ line[i] = '\0';
+ }
+ AddParameter( name, parameters );
+ }
+ /* Is there anything left? */
+ if ( i < n )
+ {
+ /* skip any spaces until the next parameter */
+ ++i; /* first skip the nul character */
+ line += i;
+ n = strlen( line );
+ for ( i = 0; ( i < n ) && utf8_isspace( line[i] ); ++i )
+ {
+ /* empty */
+ }
+ line += i;
+ n = strlen( line );
+ i = 0;
+ }
+ }
+}
+
+/*******/
+
+void GetParameter(
+ char *line,
+ struct Parameters *parameters )
+{
+ int i;
+ int n = strlen( line );
+
+ /* Remove any spaces at the end of the line */
+ for ( i = n - 1; i >= 0 && utf8_isspace( line[i] ); --i )
+ {
+ line[i] = '\0';
+ }
+ assert( i > 0 ); /* If i <= 0 then the line was empty
+ and that cannot be, because this
+ is supposed to be a parameter */
+ /* Skip any white space at the begin of the line. */
+ n = strlen( line );
+ for ( i = 0; i < n && utf8_isspace( line[i] ); ++i )
+ {
+ /* Empty */
+ }
+ line += i;
+
+ AddParameter( line, parameters );
+}
+
+
+
+
+void Free_Configuration(
+ void )
+{
+ /* TODO Deallocate custom_headertypes */
+}
+
+
+
+
+static void ComplementItemNames(
+ void )
+{
+ if ( configuration.items.number )
+ {
+ char *first_item = configuration.items.names[0];
+
+ /* The SOURCE item is always included, and should be the
+ * first one! */
+ configuration.items.names[0] = RB_StrDup( "SOURCE" );
+ AddParameter( first_item, &( configuration.items ) );
+ free( first_item );
+ }
+ else
+ {
+ /* No item names were defined, so we use the default ones */
+ unsigned int i = 0;
+
+ for ( ; default_item_names[i]; ++i )
+ {
+ AddParameter( default_item_names[i], &( configuration.items ) );
+ }
+ }
+}
+
+
+/* TODO Documentation */
+
+
+static void SecondScan(
+ FILE *f )
+{
+ char *myConfLine = NULL;
+ int readConfChars = 0;
+ T_Block_Kind section_kind = SK_UNKNOWN;
+ T_Line_Kind line_kind = CFL_UNKNOWN;
+
+ while ( !feof( f ) )
+ {
+ free( myConfLine );
+ readConfChars = 0;
+ myConfLine = RB_ReadWholeLine( f, line_buffer, &readConfChars );
+ RB_StripCR( myConfLine );
+ line_kind = ConfigLineKind( myConfLine );
+ switch ( line_kind )
+ {
+ case CFL_REMARK:
+ case CFL_EMPTYLINE: /* fall through */
+ /* Do nothing */
+ break;
+ case CFL_SECTION:
+ section_kind = BlockKind( myConfLine );
+ break;
+ case CFL_PARAMETER:
+ {
+ switch ( section_kind )
+ {
+ case SK_ITEMS:
+ GetParameter( myConfLine, &( configuration.items ) );
+ break;
+ case SK_OPTIONS:
+ GetParameters( myConfLine, &( configuration.options ) );
+ break;
+ case SK_IGNOREITEMS:
+ GetParameter( myConfLine,
+ &( configuration.ignore_items ) );
+ break;
+ case SK_SOURCE_ITEMS:
+ GetParameter( myConfLine,
+ &( configuration.source_items ) );
+ break;
+ case SK_HEADERTYPES:
+ // Store all complete lines, they will be broken down later
+ // in Install_Custom_HeaderTypes()
+ GetParameter( myConfLine,
+ &( configuration.custom_headertypes ) );
+ break;
+ case SK_IGNORE_FILES:
+ GetParameters( myConfLine,
+ &( configuration.ignore_files ) );
+ break;
+ case SK_ACCEPT_FILES:
+ GetParameters( myConfLine,
+ &( configuration.accept_files ) );
+ break;
+ case SK_HEADER_MARKERS:
+ GetParameter( myConfLine,
+ &( configuration.header_markers ) );
+ break;
+ case SK_REMARK_MARKERS:
+ GetParameter( myConfLine,
+ &( configuration.remark_markers ) );
+ break;
+ case SK_END_MARKERS:
+ GetParameter( myConfLine,
+ &( configuration.end_markers ) );
+ break;
+ case SK_REMARK_END_MARKERS:
+ GetParameter( myConfLine,
+ &( configuration.remark_end_markers ) );
+ break;
+ case SK_REMARK_BEGIN_MARKERS:
+ GetParameter( myConfLine,
+ &( configuration.remark_begin_markers ) );
+ break;
+ case SK_KEYWORDS:
+ GetParameter( myConfLine, &( configuration.keywords ) );
+ break;
+ case SK_SOURCE_LINE_COMMENTS:
+ GetParameter( myConfLine,
+ &( configuration.source_line_comments ) );
+ break;
+ case SK_HEADER_IGNORE_CHARS:
+ GetParameter( myConfLine,
+ &( configuration.header_ignore_chars ) );
+ break;
+ case SK_HEADER_SEPARATE_CHARS:
+ GetParameter( myConfLine,
+ &( configuration.header_separate_chars ) );
+ break;
+ case SK_PREFORMATTED_ITEMS:
+ GetParameter( myConfLine,
+ &( configuration.preformatted_items ) );
+ break;
+ case SK_FORMAT_ITEMS:
+ GetParameter( myConfLine,
+ &( configuration.format_items ) );
+ break;
+ case SK_ITEM_ORDER:
+ GetParameter( myConfLine,
+ &( configuration.item_order ) );
+ break;
+ case SK_UNKNOWN:
+ break;
+ default:
+ assert( 0 );
+ }
+ }
+ break;
+ case CFL_UNKNOWN:
+ default:
+ assert( 0 );
+ }
+ }
+ free( myConfLine );
+}
--- /dev/null
+#ifndef ROBODOC_CONFIG_H
+#define ROBODOC_CONFIG_H
+// vi: spell ff=unix
+//
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+typedef enum
+{
+ CFL_REMARK = 0,
+ CFL_PARAMETER,
+ CFL_SECTION,
+ CFL_EMPTYLINE,
+ CFL_UNKNOWN
+} T_Line_Kind;
+
+typedef enum
+{
+ SK_ITEMS = 0,
+ SK_IGNOREITEMS,
+ SK_OPTIONS,
+ SK_HEADERTYPES,
+ SK_IGNORE_FILES,
+ SK_ACCEPT_FILES,
+ SK_HEADER_MARKERS,
+ SK_REMARK_MARKERS,
+ SK_END_MARKERS,
+ SK_REMARK_BEGIN_MARKERS,
+ SK_REMARK_END_MARKERS,
+ SK_SOURCE_ITEMS,
+ SK_KEYWORDS,
+ SK_SOURCE_LINE_COMMENTS,
+ SK_HEADER_IGNORE_CHARS,
+ SK_HEADER_SEPARATE_CHARS,
+ SK_PREFORMATTED_ITEMS,
+ SK_FORMAT_ITEMS,
+ SK_ITEM_ORDER,
+ SK_UNKNOWN
+} T_Block_Kind;
+
+
+/****s* Configuration/keywords_hash_s
+ * FUNCTION
+ * Structure for a keyword hash table row.
+ * ATTRIBUTES
+ * o keyword -- pointer to the keyword
+ * o next -- pointer to next entry in the row
+ * SOURCE
+ */
+struct keywords_hash_s
+{
+ struct keywords_hash_s *next;
+ char *keyword;
+};
+
+/*****/
+
+
+/****s* Configuration/Parameters
+ * FUNCTION
+ * Structure to store all the paramters found in a block in the
+ * robodoc configuation file.
+ * ATTRIBUTES
+ * o number -- the number of parameters found.
+ * o size -- the maximum size of the names array.
+ * o names -- an array with the values of the parameters.
+ * NOTES
+ * Find a better name for the attribute 'names'
+ * SOURCE
+ */
+
+struct Parameters
+{
+ unsigned int number;
+ unsigned int size;
+ char **names;
+};
+
+/*****/
+
+
+/****s* Configuration/RB_Configuration
+ * FUNCTION
+ * All the data from the robodoc.rc file is stored in this
+ * structure.
+ * ATTRIBUTES
+ * o items -- an array with names that robodoc recognizes as
+ * items. Alsways includes the name "SOURCE" as
+ * the first element.
+ * o ignore_items -- an array with the names of items that ROBODoc
+ * should ignore.
+ * o source_items -- an array with the names of items that work
+ * similar to the built-in SOURCE item.
+ * o preformatted_items -- item names that will be automatically
+ * preformatted
+ * o format_items -- item names that should be formatted by the
+ * browser
+ * o item_order -- an array with item names that
+ * indicates which items should be displayed first.
+ * o options -- Array with all options specified both on the
+ * commandline as well as in the robodoc.rc file.
+ * o custom_headertypes -- list with custom header types.
+ * o ignore_files -- list with wildcard expressions that specifies
+ * files and directories that robodoc should skip
+ * while scanning the source tree.
+ * o header_markers -- list with markers that mark the begin of a
+ * header.
+ * o remark_markers -- list with markers that mark a remark.
+ * o end_markers -- list with markers that markt the end of a
+ * header.
+ * o remark_begin_markers -- list of markers that mark the begin of
+ * a remark. For instance (*
+ * o remakr_end_markers -- list of markers that mark the end of a
+ * remark. For instance *)
+ * o keywords -- source keywords to recognise (and colorise)
+ * o source_line_comments -- comment markers that span until the end of line
+ * o header_ignore_chars -- characters for beginning of header remarks
+ * o header_separate_chars -- characters that separates header artifacts
+ *
+ * SOURCE
+ */
+
+struct RB_Configuration
+{
+ struct Parameters items;
+ struct Parameters ignore_items;
+ struct Parameters source_items;
+ struct Parameters preformatted_items;
+ struct Parameters format_items;
+ struct Parameters item_order;
+
+ struct Parameters options;
+
+ struct Parameters ignore_files;
+ struct Parameters accept_files;
+
+ struct Parameters custom_headertypes;
+ struct Parameters header_markers;
+ struct Parameters remark_markers;
+ struct Parameters end_markers;
+ struct Parameters remark_begin_markers;
+ struct Parameters remark_end_markers;
+
+ struct Parameters keywords;
+ struct Parameters source_line_comments;
+ struct Parameters header_ignore_chars;
+ struct Parameters header_separate_chars;
+};
+
+/*******/
+
+char *ReadConfiguration(
+ unsigned int argc,
+ char **argv,
+ char *filename );
+void Free_Configuration(
+ void );
+void Install_C_Syntax(
+ void );
+char *Find_Keyword(
+ char *keyword,
+ int len );
+char *Find_Parameter_Exact(
+ struct Parameters *params,
+ char *paramname );
+char *Find_Parameter_Partial(
+ struct Parameters *params,
+ char *paramname );
+char *Find_Parameter_Char(
+ struct Parameters *params,
+ char param );
+
+extern struct RB_Configuration configuration;
+
+#endif
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* Docuwala/ROBODoc
+ * FUNCTION
+ * ROBODoc is intended to be a replacement for the original AutoDocs
+ * program. ROBODoc will extract the comment headers from a source
+ * file, and put them into a separate documentation file.
+ * General Flow
+ *
+ * Sourcecode ---> [ROBODoc] ---> Document.
+ *
+ * The whole ROBODoc process consists of three steps: scanning,
+ * analysing, generating.
+ *
+ * Scanning
+ *
+ * ROBODoc scans the source directory tree. This collects the names of
+ * all the source files.
+ *
+ * Analysing
+ *
+ * ROBODOc analyses all the sourcefiles. This reads the content of all
+ * source files and collects all the headers.
+ *
+ * Generating
+ *
+ * In this step the headers are written to one or more documentation files.
+ * In addition
+ *
+ *
+ * The data collected during scanning and analysing is stored in a
+ * number of structures.
+ *
+ * RB_Directory, it stores the names of the sourcefiles and directories in
+ * the source direcory tree. File names are stored in a RB_Filename
+ * structure, directory names in a RB_Path structure. Each RB_Filename
+ * contains a pointer (path) to a RB_Path structure that tells in which
+ * directory a file is located. Each RB_Path has a pointer (parent) to
+ * another RB_Path structure that tells in which directory is a directory
+ * located (of which directory it is a subdirectory). The only exception
+ * is the root directory.
+ *
+ * Besides the name of the sourcefile, the RB_Filename also stores the
+ * name of the documentation file.
+ *
+ * For each sourcefile there is an RB_Part structure. It contains a
+ * pointer (filename) to the RB_Filename and a list (headers) of
+ * RB_Header structure containing the headers found in the sourcefile.
+ *
+ * Every RB_Header structure contains a pointer (owner) to the RB_Part
+ * structure to which it belongs. Headers can form a hierarchy that is
+ * used to create sections and subsections in the documentation. To
+ * store this hierarchy every RB_header structure contains a pointer
+ * (parent) to its parent header. For instance, given the following two
+ * headers, SubModule is the parent of SubSubModule.
+ * ****h* TopModule/SubModule
+ * *
+ * ****
+ *
+ * ****h* SubModule/SubSubModule
+ * *
+ * ****
+ *
+ * In the documentation this creates the sections
+ * 1.TopModule
+ * 1.1 SubModule
+ * 1.1.1 SubSubModule
+ *
+ * The RB_Directory and the linked list of RB_Part structures are
+ * stored in a RB_Document structure.
+ *
+ * During the generation of the documentation ROBODoc tries to create
+ * cross links between the mention of a header's name (an object) and the
+ * documentation generated from that header (the documentation for the
+ * object).
+ *
+ * To aid this proces there is an array of RB_link structures. This
+ * array is sorted for quick searching. RB_link structures the name
+ * of a header and the name of the label under which the documentation
+ * can be found in the documentation file.
+ *
+ * AUTHOR
+ * See AUTHORS in the archive.
+ * CREATION DATE
+ * 20-Dec-94 Jacco van Weert.
+ * MODIFICATION HISTORY
+ * See ChangeLog in the archive. Latest version can be found on:
+ * o http://www.xs4all.nl/~rfsber/Robo/
+ * BUGS
+ * Other bugs? Catch them in a jar and send them to rfsber -(at)- xs4all.nl
+ *
+ ****
+ * $Id: robodoc.c,v 1.106 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <locale.h>
+#ifdef __APPLE__
+#include <unistd.h>
+#endif
+
+#include "robodoc.h"
+#include "globals.h"
+#include "headers.h"
+#include "util.h"
+#include "links.h"
+#include "part.h"
+#include "analyser.h"
+#include "generator.h"
+#include "document.h"
+#include "directory.h"
+#include "roboconfig.h"
+#include "optioncheck.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+/* Functions local to this file. */
+
+T_RB_DocType Find_DocType(
+ void );
+
+actions_t Find_Actions(
+ void );
+
+long Find_DebugMode(
+ void );
+
+int Find_Option(
+ char *option );
+
+static int PathBegin_Check(
+ char *path );
+
+static void Path_Check(
+ char *sourcepath,
+ char *docpath );
+
+char *Find_Parameterized_Option(
+ char *actionname );
+
+char *RB_Find_In_Argv_Parameterized_Option(
+ int argc,
+ char **argv,
+ char *optionname );
+
+static char *General_Find_Parameterized_Option(
+ int n,
+ char **options,
+ char *optionname );
+
+static char *Fix_Path(
+ char *path );
+
+static char *Path_Convert_Win32_to_Unix(
+ char *path );
+
+static char *Find_And_Fix_Path(
+ char *option_name );
+
+static void RB_Summary(
+ struct RB_Document *document );
+
+static void Find_Tabstops(
+ void );
+
+static void Find_And_Install_Document_Name(
+ char *option,
+ unsigned char type );
+
+char RB_VER[] =
+ "$VER: robodoc " VERSION " (" __DATE__
+ ") (c) by Maverick Software Development 1994-2006";
+
+
+/****h* ROBODoc/UserInterface
+ * FUNCTION
+ * This module contains functions to parse the
+ * command line and inform the user about any errors.
+ *****
+ */
+
+
+/****v* UserInterface/use
+ * NAME
+ * use -- usage string
+ * FUNCTION
+ * Inform the user how to use ROBODoc.
+ * AUTHOR
+ * Koessi
+ * SOURCE
+ */
+
+char use[] =
+// 1 2 3 4 5 6 7 8
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+ "ROBODoc Version " VERSION " autodocs formatter ($Revision: 1.106 $)\n"
+ "(c) 1994-2007 Frans Slothouber, Petteri Kettunen, \n"
+ " Gergely Budai and Jacco van Weert\n"
+ "ROBODoc comes with ABSOLUTELY NO WARRANTY.\n"
+ "This is free software, and you are welcome to redistribute it\n"
+ "under certain conditions; type `robodoc -c' for details.\n" "\n";
+char use_usage[] =
+ "Usage:\n"
+ " robodoc --src <directory> --doc <directory> --multidoc [type] [options]\n"
+ " robodoc --src <directory> --doc <file> --singledoc [type] [options]\n"
+ " robodoc --src <file> --doc <file> --singlefile [type] [options]\n"
+ "\n"
+ "Type:\n" " --html, --rtf, --latex, --ascii, --dbxml, --troff\n" "\n";
+char use_options1[] =
+ "Options:\n"
+ " --charset NAME Add character encoding information (html only).\n"
+ " --cmode Use ANSI C grammar in source items (html only).\n"
+ " --css Specify the stylesheet to use.\n"
+ " --ext EXTENSION Set extension for generated files.\n"
+ " --footless Do not create the foot of a document.\n"
+ " --headless Do not create the head of a document.\n"
+ " --index Add an index.\n";
+char use_options2[] =
+ " --internal Also include internal headers.\n"
+ " --internalonly Only include internal headers.\n"
+ " --lock Recognize only one header marker per file.\n"
+ " --nodesc Do not descent into subdirectories.\n"
+ " --no_subdirectories\n"
+ " Do no create documentation subdirectories.\n"
+ " --nopre Do not use <PRE> </PRE> in the HTML output.\n"
+ " --nosort Do not sort the headers.\n"
+ " --nosource Do not include SOURCE items.\n"
+ " --nogeneratedwith\n"
+ " Do not add the 'generated by robodoc' message\n"
+ " at the top of each documentation file.\n"
+ " --ignore_case_when_linking\n"
+ " Ignore the case of the symbols when trying\n"
+ " to create crosslinks.\n";
+char use_options3[] =
+ " --rc Specify an alternate configuration file.\n"
+ " --sections Add sections and subsections.\n"
+ " --first_section_level NUMBER\n"
+ " Start the first section not at 1 but at \n"
+ " level NUMBER.\n"
+ " --tabsize NUMBER Set the tab size.\n"
+ " --tabstops a,b,..,n\n"
+ " Set TAB stops \n"
+ " --tell ROBODoc will tell you what it is doing.\n"
+ " --debug same as --tell, but with lots more details.\n"
+ " --toc Add a table of contents.\n"
+ " --version Print version info and exit.\n"
+ " --sectionnameonly\n"
+ " Generate section header with name only.\n"
+ " --compress Only supported by TROFF output format. Defines by which\n"
+ " program manpages will be compressed. Either bzip2 or gzip.\n"
+ " --mansection Manual section where pages will be inserted (default: 3).\n"
+ " --documenttitle TITLE\n"
+ " Set the document title\n"
+ " --altlatex Alternate LaTeX file format (bigger / clearer than normal)\n"
+ " --latexparts Make the first module level as PART in LaTeX output\n"
+ " --syntaxcolors Turn on all syntax highlighting features in SOURCE items\n"
+ " (html only)\n"
+ " --syntaxcolors_enable quotes,squotes,line_comments,block_comments,\n"
+ " keywords,non_alpha\n"
+ " Enable only specific syntax highlighting features in\n"
+ " SOURCE items (html only)\n"
+ " --dotname NAME Specify the name (and path / options) of DOT tool\n"
+ " --masterindex title,filename\n"
+ " Specify the tile and filename for master index page\n"
+ " --sourceindex title,filename\n"
+ " Specify the tile and filename for source files index page\n"
+ " --one_file_per_header\n"
+ " Create a separate documentation file for each header\n"
+ " --header_breaks NUMBER\n"
+ " Insert a linebreak after every NUMBER header names\n"
+ " (default value: 2, set to zero to disable)\n" "\n";
+char use_authors[] =
+ "Authors/Contributors:\n"
+ " Frans Slothouber, Jacco van Weert, Petteri Kettunen, Bernd Koesling,\n"
+ " Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner, Sasha Vasko,\n"
+ " Kai Hofmann, Thierry Pierron, Friedrich Haase, Gergely Budai.\n";
+/********/
+
+
+/****v* UserInterface/copying
+ * FUNCTION
+ * Information about the ROBODoc licence.
+ * AUTHOR
+ * Frans
+ * HISTORY
+ * 2003-02-25/petterik: corrected link to GNU copyleft.
+ *******
+ */
+
+char copying[] =
+ "\n"
+ " Distributed under the GNU GENERAL PUBLIC LICENSE\n"
+ " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
+ " See the source archive for a copy of the complete licence\n"
+ " If you do not have it you can get it from URL\n"
+ " http://www.gnu.org/copyleft/gpl.html\n";
+
+
+static void dump_version(
+ void )
+{
+ printf( "%s\n", VERSION );
+ /* TODO create an option to show robodoc configuration,
+ * including compiler setting etc..
+ printf( "Configuration:\n" );
+ printf( " locale: %s", setlocale( LC_ALL, "" ) );
+ */
+}
+
+/* TODO This function is too long. */
+
+/****f* UserInterface/main
+ * FUNCTION
+ * Get and parse the arguments. Analyse document and generate the
+ * documentation. Everything starts from here.
+ * SYNOPSIS
+ */
+int main(
+ int argc,
+ char **argv )
+/*
+ * SOURCE
+ */
+{
+ struct RB_Document *document = NULL;
+ struct RB_Directory *srctree = NULL;
+ char *optstr = NULL;
+ char *used_rc_file = NULL;
+ long debug = 0;
+
+/*
+ TODO, make setlocale work.
+ char * loc;
+
+ if ( (loc = getenv("LC_CTYPE") ) != NULL )
+ {
+ printf( ".... %s\n", loc );
+ setlocale( LC_ALL, loc);
+ }
+ else
+ {
+ setlocale(LC_ALL, "");
+ }
+*/
+
+ whoami = argv[0]; /* global me,myself&i */
+
+ // Init Actions global
+ course_of_action = No_Actions( );
+
+ /* Read the configuration file. This might contain
+ addition options. */
+ RB_SetCurrentFile( NULL );
+ used_rc_file = ReadConfiguration( argc, argv,
+ RB_Find_In_Argv_Parameterized_Option
+ ( argc, argv, "--rc" ) );
+
+ /* Force debug mode early if the user wants the debug mode */
+ debugmode = Find_DebugMode( );
+ course_of_action.do_tell = TRUE;
+
+ if ( Check_Options( ) == EXIT_FAILURE )
+ {
+
+ return EXIT_FAILURE;
+ }
+
+ if ( Find_Option( "-c" ) )
+ {
+ printf( "%s", copying );
+ return EXIT_SUCCESS;
+ }
+
+ if ( Find_Option( "--version" ) )
+ {
+ dump_version( );
+ return EXIT_SUCCESS;
+ }
+
+ if ( Find_Option( "--help" ) )
+ {
+ printf( "%s%s%s%s%s%s", use, use_usage, use_options1, use_options2,
+ use_options3, use_authors );
+ return EXIT_SUCCESS;
+ }
+
+ output_mode = Find_DocType( ); /* one of the globals that are still left */
+ if ( output_mode == UNKNOWN )
+ {
+ Print_Short_Use( );
+ return EXIT_SUCCESS;
+ }
+
+ /* First the basics. */
+ document = RB_Get_RB_Document( );
+ document->doctype = output_mode;
+ document->actions = Find_Actions( );
+ debug = document->debugmode = Find_DebugMode( );
+ document->charset = Find_Parameterized_Option( "--charset" );
+ document->extension = Find_Parameterized_Option( "--ext" );
+ document->css = Find_Parameterized_Option( "--css" );
+ document->compress = Find_Parameterized_Option( "--compress" );
+ document->section = Find_Parameterized_Option( "--mansection" );
+ document_title = Find_Parameterized_Option( "--documenttitle" );
+ optstr = Find_Parameterized_Option( "--first_section_level" );
+ if ( optstr )
+ {
+ document->first_section_level = atoi( optstr );
+ }
+
+ course_of_action = document->actions; /* a global */
+ debugmode = document->debugmode; /* a global */
+
+ RB_Say( "Using %s for defaults\n", SAY_INFO, used_rc_file );
+ free( used_rc_file ); /* No longer necessary */
+ used_rc_file = NULL;
+
+ if ( document->css )
+ {
+ document->css = Path_Convert_Win32_to_Unix( document->css );
+ }
+
+ if ( ( document->actions.do_index ) && output_mode == TROFF )
+ {
+ RB_Warning( "Index generation not supported for TROFF format.\n" );
+ document->actions.do_index = FALSE;
+ }
+
+ if ( Find_Parameterized_Option( "--doctype_name" ) &&
+ Find_Parameterized_Option( "--doctype_location" ) )
+ {
+ document->doctype_name =
+ Find_Parameterized_Option( "--doctype_name" );
+ document->doctype_location =
+ Find_Parameterized_Option( "--doctype_location" );
+ }
+
+ // Find tab sizes and tab stops
+ Find_Tabstops( );
+
+ // Find master index file name
+ Find_And_Install_Document_Name( "--masterindex", HT_MASTERINDEXTYPE );
+
+ // Find source index file name
+ Find_And_Install_Document_Name( "--sourceindex", HT_SOURCEHEADERTYPE );
+
+ // Find DOT tool name
+ optstr = Find_Parameterized_Option( "--dotname" );
+ if ( optstr )
+ {
+ dot_name = optstr;
+ }
+
+ // Find number of headers before linebreak
+ optstr = Find_Parameterized_Option( "--header_breaks" );
+ if ( optstr )
+ {
+ int breaks = atoi( optstr );
+
+ if ( breaks == 0 )
+ {
+ breaks = MAX_HEADER_BREAKS;
+ }
+ header_breaks = breaks;
+ }
+
+ // Get extension
+ if ( !document->extension )
+ {
+ document->extension = RB_Get_Default_Extension( document->doctype );
+ }
+
+
+ /* Test if there is a --src and --doc */
+ if ( !Find_Parameterized_Option( "--src" ) )
+ {
+ printf( "Error: you need to specify a source"
+ " file or directory with --src.\n" );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ }
+ if ( !Find_Parameterized_Option( "--doc" ) )
+ {
+ printf( "Error: you need to specify a documentation file"
+ " or directory with --doc.\n" );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ }
+
+ /* What mode are we using? */
+ if ( Find_Option( "--multidoc" ) )
+ {
+ char *srcrootname; /* directory */
+ char *docrootname;
+
+ srcrootname = Find_And_Fix_Path( "--src" );
+ if ( !Stat_Path( 'e', srcrootname ) )
+ {
+ printf( "Error: %s does not exists\n", srcrootname );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ }
+
+ if ( !Stat_Path( 'd', srcrootname ) )
+ {
+ printf( "Error: %s is not a directory\n", srcrootname );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ }
+ document->srcroot = RB_Get_RB_Path( srcrootname );
+
+ docrootname = Find_And_Fix_Path( "--doc" );
+ Path_Check( srcrootname, docrootname );
+
+ document->docroot = RB_Get_RB_Path( docrootname );
+
+ srctree = RB_Get_RB_Directory( srcrootname, docrootname );
+ document->srctree = srctree;
+
+ RB_Document_Create_Parts( document );
+ RB_Analyse_Document( document );
+ RB_Generate_Documentation( document );
+
+ RB_Free_RB_Path( document->srcroot );
+ document->srcroot = 0;
+ RB_Free_RB_Path( document->docroot );
+ document->docroot = 0;
+ RB_Free_RB_Directory( srctree );
+ document->srctree = 0;
+ }
+ else if ( output_mode == TROFF )
+ {
+ RB_Panic( "Only --multidoc is supported for TROFF output.\n" );
+ }
+ else if ( Find_Option( "--singledoc" ) )
+ {
+ char *srcrootname; /* directory */
+
+ srcrootname = Find_And_Fix_Path( "--src" );
+ if ( !Stat_Path( 'e', srcrootname ) )
+ {
+ printf( "Error: %s does not exists\n", srcrootname );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ }
+
+ if ( !Stat_Path( 'd', srcrootname ) )
+ {
+ printf( "Error: %s is not a directory\n", srcrootname );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ };
+ document->srcroot = RB_Get_RB_Path( srcrootname );
+
+ document->docroot = 0;
+ document->singledoc_name = Find_And_Fix_Path( "--doc" );
+
+ srctree = RB_Get_RB_Directory( srcrootname, NULL );
+ document->srctree = srctree;
+
+ RB_Document_Create_Parts( document );
+ RB_Analyse_Document( document );
+ RB_Generate_Documentation( document );
+
+ RB_Free_RB_Directory( srctree );
+ }
+ else if ( Find_Option( "--singlefile" ) )
+ {
+ char *srcfile; /* file */
+ char *docfile; /* file */
+
+ document->docroot = 0;
+ docfile = Find_And_Fix_Path( "--doc" );
+ document->singledoc_name = docfile;
+ srcfile = Find_And_Fix_Path( "--src" );
+ if ( !Stat_Path( 'e', srcfile ) )
+ {
+ printf( "Error: %s does not exists\n", srcfile );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ };
+
+ if ( !Stat_Path( 'f', srcfile ) )
+ {
+ printf( "Error: %s is not a file\n", srcfile );
+ Print_Short_Use( );
+ return EXIT_FAILURE;
+ };
+
+ document->srctree = RB_Get_RB_SingleFileDirectory( srcfile );
+ document->srcroot =
+ RB_Get_RB_Path( document->srctree->first_path->name );
+
+ RB_Document_Create_Parts( document );
+ RB_Analyse_Document( document );
+ RB_Generate_Documentation( document );
+
+ RB_Free_RB_Directory( document->srctree );
+ }
+ else
+ {
+ Print_Short_Use( );
+ printf
+ ( "\n\nError: Use either --multidoc, --singledoc, or --singlefile\n" );
+ return EXIT_FAILURE;
+ }
+
+ RB_Summary( document );
+ RB_Free_RB_Document( document );
+ Free_Configuration( );
+
+#ifdef __APPLE__
+ /* Mac OS X specific: print memory leaks */
+ if ( debug & SAY_DEBUG )
+ {
+ char cmd[32];
+
+ sprintf( cmd, "/usr/bin/leaks %d", getpid( ) );
+ system( cmd );
+ }
+#endif /* __APPLE__ */
+
+ return EXIT_SUCCESS;
+}
+
+/*******/
+
+
+/****if* UserInterface/Find_Tabstops
+ * FUNCTION
+ * This function looks for the tab stop configuration and fills out the
+ * tab_stops table.
+ * SYNOPSIS
+ */
+static void Find_Tabstops(
+ void )
+/*
+ * SOURCE
+ */
+{
+ int i;
+ int tabsize = DEFAULT_TABSIZE;
+ char *optstr, *str;
+
+ // Find tab sizes
+ optstr = Find_Parameterized_Option( "--tabsize" );
+ if ( optstr )
+ {
+ tabsize = atoi( optstr );
+ }
+
+ // Fill tabstop table with default values
+ for ( i = 0; i < MAX_TABS; i++ )
+ {
+ tab_stops[i] = tabsize * ( i + 1 );
+ }
+
+ // Find tab stops
+ optstr = Find_Parameterized_Option( "--tabstops" );
+ if ( optstr )
+ {
+ // Get TAB sizes and fill table
+ for ( str = strtok( optstr, TABSIZE_SEPARATOR ), i = 0;
+ str != NULL && i < MAX_TABS;
+ str = strtok( NULL, TABSIZE_SEPARATOR ), i++ )
+ {
+ tab_stops[i] = atoi( str ) - 1;
+ }
+ }
+}
+
+/*******/
+
+
+/****if* UserInterface/Find_And_Install_Document_Name
+ * FUNCTION
+ * Looks for specific option strings and installs special document titles
+ * and filenames.
+ *
+ * Should be used to overwrite the HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE
+ * entries in the header_type_lookup_table.
+ * SYNOPSIS
+ */
+static void Find_And_Install_Document_Name(
+ char *option,
+ unsigned char type )
+/*
+ * INPUTS
+ * o option -- the option string to look for
+ * o type -- the type entry in header_type_lookup_table to overwrite
+ * (Should be either HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE)
+ *
+ * SOURCE
+ */
+{
+ char *title = NULL, *filename = NULL, *optstr = NULL;
+
+ // First find the option
+ optstr = Find_Parameterized_Option( option );
+ if ( optstr )
+ {
+
+ // Break the option into substrings
+ title = strtok( optstr, "," );
+ filename = strtok( NULL, "," );
+
+ // If something missing
+ if ( title == NULL || filename == NULL )
+ {
+ RB_Panic( "Invalid %s option\n", option );
+ }
+
+ // Install document title and filename
+ RB_AddHeaderType( type, title, filename, 0 );
+ }
+}
+
+/*******/
+
+
+
+/****f* UserInterface/Find_And_Fix_Path
+ * FUNCTION
+ * Searches through the options to find a path.
+ * This path is converted to a propper path
+ * if it contains errors such as the use of
+ * '\' or when it does not start with ./ or a
+ * drive name.
+ * The option must exist.
+ * SYNOPSIS
+ */
+static char *Find_And_Fix_Path(
+ char *option_name )
+/*
+ * INPUTS
+ * o option_name -- the option name for the path
+ * RESULT
+ * o path -- the path.
+ *
+ * SOURCE
+ */
+{
+ char *temp;
+ char *temp2;
+
+ temp = Find_Parameterized_Option( option_name );
+ assert( temp );
+ temp = Path_Convert_Win32_to_Unix( temp );
+ temp2 = Fix_Path( temp );
+ free( temp );
+ return temp2;
+}
+
+/*******/
+
+
+/****f* UserInterface/Path_Convert_Win32_to_Unix
+ * FUNCTION
+ * Although people are supposed to specify all paths
+ * with a '/' as seperator, sometimes people on Win32
+ * use '\', this causes problems later on in some
+ * other function of robodoc that expect a '/'.
+ * So to prevent this we replace all the '\' in a path
+ * with '/'
+ *
+ * In addition people sometimes add a '/' at the
+ * end of the path. We remove it.
+ *
+ * SYNOPSIS
+ */
+static char *Path_Convert_Win32_to_Unix(
+ char *path )
+/*
+ * INPUTS
+ * o path -- the path.
+ * RESULT
+ * o path -- the converted path (in a newly allocated
+ * block of memory).
+ * SOURCE
+ */
+{
+ size_t i;
+
+ /* First make a copy */
+ path = RB_StrDup( path );
+ for ( i = 0; i < strlen( path ); ++i )
+ {
+ if ( path[i] == '\\' )
+ {
+ path[i] = '/';
+ }
+ }
+
+ /* Remove trailing '/' if there is one. */
+ if ( path[strlen( path ) - 1] == '/' )
+ {
+ path[strlen( path ) - 1] = '\0';
+ }
+
+ return path;
+}
+
+/*****/
+
+/****f* UserInterface/Path_Check
+ * FUNCTION
+ * Test the validity of the doc and source path. The doc path should
+ * not be a sub directory of the source path otherwise the generated
+ * documentation will be part of the generated documentation if
+ * robodoc is run more than once.
+ * SYNOPSIS
+ */
+
+static void Path_Check(
+ char *sourcepath,
+ char *docpath )
+/*
+ * INPUTS
+ * o sourcepath -- the path to the source files.
+ * o docpath -- the path to the documentation files.
+ * OUTPUT
+ * o error messages
+ * SOURCE
+ */
+{
+ if ( docpath )
+ {
+ int dl;
+ int sl;
+
+ dl = strlen( docpath );
+ sl = strlen( sourcepath );
+ if ( dl >= sl )
+ {
+ int i;
+ int equal = TRUE;
+
+ for ( i = 0; i < sl; ++i )
+ {
+ if ( docpath[i] != sourcepath[i] )
+ {
+ equal = FALSE;
+ break;
+ }
+ }
+ if ( equal && ( dl == sl ) )
+ {
+ RB_Panic
+ ( "The source path and document path can not be equal\n" );
+ }
+ else
+ {
+ /* OK */
+ }
+ }
+ }
+}
+
+/*****/
+
+
+/****f* UserInterface/PathBegin_Check
+ * FUNCTION
+ * Checks the validity of a path.
+ * A path should start with
+ * ./
+ * or
+ * /
+ * or
+ * have a ':' some where
+ * SYNOPSIS
+ */
+static int PathBegin_Check(
+ char *path )
+/*
+ * INPUTS
+ * o path -- the path to be cheked.
+ * RESULT
+ * o FALSE -- path is not OK.
+ * o TRUE -- path is OK.
+ * SOURCE
+ */
+{
+ int result = FALSE;
+ int l = strlen( path );
+
+ if ( l == 1 )
+ {
+ result = ( path[0] == '.' );
+ }
+ else if ( l >= 2 )
+ {
+ result = ( ( path[0] == '.' ) && ( path[1] == '/' ) ) ||
+ ( path[0] == '/' ) || ( strchr( path, ':' ) != NULL );
+ }
+ else
+ {
+ /* Empty */
+ }
+ return result;
+}
+
+/******/
+
+
+
+/****f* UserInterface/Find_Option
+ * FUNCTION
+ * Search configuration.options for a specific option.
+ * SYNOPSIS
+ */
+int Find_Option(
+ char *option )
+/* INPUTS
+ * o option -- the option to be found.
+ * RESULT
+ * o TRUE -- option does exist
+ * o FALSE -- option does not exist
+ * SOURCE
+ */
+{
+ unsigned int parameter_nr;
+ int found = FALSE;
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ if ( !RB_Str_Case_Cmp
+ ( configuration.options.names[parameter_nr], option ) )
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ return found;
+}
+
+/******/
+
+/****f* UserInterface/Fix_Path
+ * FUNCTION
+ * Add a "./" to a path if it does not start with a "./" or does not
+ * contain a ":". If the path was "." just add a "/". Adding a
+ * "./" simplifies the creating of relative links during the
+ * generation process.
+ * SYNOPSIS
+ */
+
+static char *Fix_Path(
+ char *path )
+/*
+ * INPUTS
+ * o path -- the path to be fixed.
+ * RESULT
+ * A pointer to a newly allocated string containing the path.
+ * SOURCE
+ */
+{
+ char *result = 0;
+
+ if ( !PathBegin_Check( path ) )
+ {
+ char *prefix = "./";
+
+ if ( strcmp( path, "." ) == 0 )
+ {
+ result = RB_StrDup( prefix );
+ }
+ else
+ {
+ int l = strlen( path );
+
+ l += strlen( prefix ) + 1;
+ result = malloc( l );
+ assert( result );
+ result[0] = '\0';
+ strcat( result, prefix );
+ strcat( result, path );
+ }
+ }
+ else
+ {
+ result = RB_StrDup( path );
+ }
+ return result;
+}
+
+/*****/
+
+
+/* TODO: FS Documentation */
+
+T_RB_DocType Find_DocType(
+ void )
+{
+ T_RB_DocType doctype = UNKNOWN;
+ unsigned int parameter_nr;
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+
+ if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--html" ) )
+ {
+ doctype = HTML;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--latex" ) )
+ {
+ doctype = LATEX;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--ascii" ) )
+ {
+ doctype = ASCII;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--rtf" ) )
+ {
+ doctype = RTF;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--test" ) )
+ {
+ doctype = TEST;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--troff" ) )
+ {
+ doctype = TROFF;
+ break;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--dbxml" ) )
+ {
+ doctype = XMLDOCBOOK;
+ break;
+ }
+ else
+ {
+ /* Ignore */
+ }
+ }
+ return doctype;
+}
+
+
+actions_t No_Actions(
+ void )
+{
+ actions_t actions;
+ unsigned int i;
+ unsigned char *actptr;
+
+ for ( i = 0, actptr = ( unsigned char * ) &actions;
+ i < sizeof( actions ); i++, actptr++ )
+ {
+ *actptr = 0;
+ }
+
+ return actions;
+}
+
+/* TODO: FS Documentation */
+actions_t Find_Actions(
+ void )
+{
+ actions_t actions;
+ unsigned int parameter_nr;
+ char *optstr;
+
+ actions = No_Actions( );
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--singledoc" ) )
+ {
+ actions.do_singledoc = TRUE;
+ }
+ if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--singlefile" ) )
+ {
+ actions.do_singlefile = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--multidoc" ) )
+ {
+ actions.do_multidoc = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--no_subdirectories" ) )
+ {
+ actions.do_no_subdirectories = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--one_file_per_header" ) )
+ {
+ actions.do_one_file_per_header = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--sections" ) )
+ {
+ actions.do_sections = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--internal" ) )
+ {
+ actions.do_include_internal = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--ignore_case_when_linking" ) )
+ {
+ actions.do_ignore_case_when_linking = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--internalonly" ) )
+ {
+ actions.do_internal_only = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--toc" ) )
+ {
+ actions.do_toc = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--index" ) )
+ {
+ actions.do_index = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--nosource" ) )
+ {
+ actions.do_nosource = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--tell" ) )
+ {
+ actions.do_tell = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--debug" ) )
+ {
+ actions.do_tell = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--nodesc" ) )
+ {
+ actions.do_nodesc = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--nogeneratedwith" ) )
+ {
+ actions.do_nogenwith = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--cmode" ) )
+ {
+ actions.do_quotes = TRUE;
+ actions.do_squotes = TRUE;
+ actions.do_line_comments = TRUE;
+ actions.do_block_comments = TRUE;
+ actions.do_keywords = TRUE;
+ actions.do_non_alpha = TRUE;
+
+ Install_C_Syntax( );
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--lock" ) )
+ {
+ actions.do_lockheader = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--footless" ) )
+ {
+ actions.do_footless = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--verbal" ) )
+ {
+ actions.do_verbal = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--ms_errors" ) )
+ {
+ actions.do_ms_errors = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--headless" ) )
+ {
+ actions.do_headless = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--nosort" ) )
+ {
+ actions.do_nosort = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--nopre" ) )
+ {
+ actions.do_nopre = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--sectionnameonly" ) )
+ {
+ actions.do_sectionnameonly = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--altlatex" ) )
+ {
+ actions.do_altlatex = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--latexparts" ) )
+ {
+ actions.do_latexparts = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--syntaxcolors" ) )
+ {
+ actions.do_quotes = TRUE;
+ actions.do_squotes = TRUE;
+ actions.do_line_comments = TRUE;
+ actions.do_block_comments = TRUE;
+ actions.do_keywords = TRUE;
+ actions.do_non_alpha = TRUE;
+ }
+ else
+ {
+ /* Not an action */
+ }
+ }
+
+ // Find specific syntax colors enable options
+ optstr = Find_Parameterized_Option( "--syntaxcolors_enable" );
+ if ( optstr )
+ {
+ char *str;
+
+ // Parse options
+ for ( str = strtok( optstr, "," ); str; str = strtok( NULL, "," ) )
+ {
+ if ( !RB_Str_Case_Cmp( str, "quotes" ) )
+ {
+ actions.do_quotes = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( str, "squotes" ) )
+ {
+ actions.do_squotes = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( str, "line_comments" ) )
+ {
+ actions.do_line_comments = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( str, "block_comments" ) )
+ {
+ actions.do_block_comments = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( str, "keywords" ) )
+ {
+ actions.do_keywords = TRUE;
+ }
+ else if ( !RB_Str_Case_Cmp( str, "non_alpha" ) )
+ {
+ actions.do_non_alpha = TRUE;
+ }
+ else
+ {
+ // Bad option specified
+ RB_Panic( "Invalid --syntaxcolors_enable option: %s\n", str );
+ }
+ }
+ }
+
+ return actions;
+}
+
+/* TODO: FS Documentation */
+long Find_DebugMode(
+ void )
+{
+ long modes = 0;
+ unsigned int parameter_nr;
+
+ for ( parameter_nr = 0;
+ parameter_nr < configuration.options.number; parameter_nr++ )
+ {
+ if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--debug" ) )
+ {
+ modes |= SAY_INFO;
+ modes |= SAY_DEBUG;
+ }
+ else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
+ "--tell" ) )
+ {
+ modes |= SAY_INFO;
+ }
+ }
+ return modes;
+}
+
+/****f* UserInterface/Find_Parameterized_Option
+ * FUNCTION
+ * Search for an option of the form
+ * --a_option_name a_value
+ * in configuration.options.
+ * SYNOPSIS
+ */
+char *Find_Parameterized_Option(
+ char *optionname )
+/*
+ * INPUTS
+ * optionname -- the name of the option to search for.
+ * RESULT
+ * NULL if the option is not found, a pointer to the value
+ * otherwise.
+ * NOTES
+ * Results in a Panic if the option is found but
+ * no value is specified.
+ * SOURCE
+ */
+{
+ return General_Find_Parameterized_Option( configuration.options.number,
+ &( configuration.options.
+ names[0] ), optionname );
+}
+
+/******/
+
+
+/****f* UserInterface/RB_Find_In_Argv_Parameterized_Option
+ * FUNCTION
+ * Search for an option of the form
+ * --a_option_name a_value
+ * in argv. The function is used to look for the
+ * --rc
+ * option that can be used to specify an
+ * alternate robodoc configuration file.
+ * SYNOPSIS
+ */
+char *RB_Find_In_Argv_Parameterized_Option(
+ int argc,
+ char **argv,
+ char *optionname )
+/*
+ * INPUTS
+ * * argc -- the argument count as received by main().
+ * * argv -- the array of argument values as received by main()
+ * * optionname -- the name of the option to search for.
+ * RESULT
+ * NULL if the option is not found, a pointer to the value
+ * otherwise.
+ * NOTES
+ * Results in a Panic if the option is found but
+ * no value is specified.
+ * SOURCE
+ */
+{
+ return General_Find_Parameterized_Option( argc, argv, optionname );
+}
+
+/*****/
+
+
+/****f* UserInterface/General_Find_Parameterized_Option
+ * FUNCTION
+ * Search for an option of the form
+ * --a_option_name a_value
+ * SYNOPSIS
+ */
+static char *General_Find_Parameterized_Option(
+ int n,
+ char **options,
+ char *optionname )
+/*
+ * INPUTS
+ * o n -- the number of options in the options array.
+ * o options -- the options array
+ * o optionname -- the name of the option to search for.
+ * RESULT
+ * NULL if the option is not found, a pointer to the value
+ * otherwise.
+ * NOTES
+ * Results in a Panic if the option is found but
+ * no value is specified.
+ * SOURCE
+ */
+{
+ int parameter_nr;
+ char *value = NULL;
+
+ for ( parameter_nr = 0; parameter_nr < n; parameter_nr++ )
+ {
+ if ( !RB_Str_Case_Cmp( options[parameter_nr], optionname ) )
+ {
+ if ( parameter_nr < n - 1 )
+ {
+ value = options[parameter_nr + 1];
+ if ( ( value[0] == '-' ) && ( value[1] == '-' ) )
+ {
+ value = NULL;
+ }
+ }
+ else
+ {
+ /* to few parameters. */
+ }
+ if ( !value )
+ {
+ RB_Panic( "you must be more specific"
+ " with the %s option\n", optionname );
+ }
+ }
+ }
+ return value;
+}
+
+/******/
+
+
+/* TODO Documentation */
+
+static void RB_Summary(
+ struct RB_Document *document )
+{
+ USE( document );
+#if 0
+ printf( "Ready\n" );
+ if ( document )
+ {
+ printf( "Found %d headers\n", document->no_headers );
+ }
+ if ( number_of_warnings )
+ {
+ printf( "Found %d warnings\n", number_of_warnings );
+ }
+#endif
+}
--- /dev/null
+#ifndef ROBODOC_ROBODOC_H
+#define ROBODOC_ROBODOC_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef VERSION
+#define VERSION "4.99.36"
+#endif
+
+#define COMMENT_ROBODOC \
+ "Generated with ROBODoc Version " VERSION " (" __DATE__ ")\n"
+#define COMMENT_COPYRIGHT\
+ "ROBODoc (c) 1994-2007 by Frans Slothouber and many others.\n"
+
+// Semaphore bits for actions
+typedef struct actions_s
+{
+ // General options
+ int do_nosort:1;
+ int do_nodesc:1;
+ int do_toc:1;
+ int do_include_internal:1;
+ int do_internal_only:1;
+ int do_tell:1;
+ int do_index:1;
+ int do_nosource:1;
+ int do_robo_head:1;
+ int do_sections:1;
+ int do_lockheader:1;
+ int do_footless:1;
+ int do_headless:1;
+ int do_nopre:1;
+ int do_ignore_case_when_linking:1;
+ int do_nogenwith:1;
+ int do_sectionnameonly:1;
+ int do_verbal:1;
+ int do_ms_errors:1;
+
+ // Document modes
+ int do_singledoc:1;
+ int do_multidoc:1;
+ int do_singlefile:1;
+ int do_one_file_per_header:1;
+ int do_no_subdirectories:1;
+
+ // Latex options
+ int do_altlatex:1;
+ int do_latexparts:1;
+
+ // Syntax coloring
+ int do_quotes:1;
+ int do_squotes:1;
+ int do_line_comments:1;
+ int do_block_comments:1;
+ int do_keywords:1;
+ int do_non_alpha:1;
+
+} actions_t;
+
+/* RB_Say modes */
+#define SAY_DEBUG (1<<0)
+#define SAY_INFO (1<<1)
+
+
+/* Output Modes */
+
+/****t* Generator/T_RB_DocType
+ * FUNCTION
+ * Enumeration for the various output formats that are
+ * supported by ROBODoc.
+ * NOTES
+ * These should be prefixed with RB_
+ * SOURCE
+ */
+
+typedef enum
+{
+ TEST = 1, /* Special output mode for testing */
+ ASCII,
+ HTML,
+ LATEX,
+ RTF,
+ TROFF,
+ XMLDOCBOOK,
+ /* SIZE_MODES, */
+ /* Reserved for Future Use */
+ /* ANSI, */
+ /* GNUINFO, */
+ /* XML, */
+ UNKNOWN
+} T_RB_DocType;
+
+/*****/
+
+
+#define USE( x ) ( x = x );
+
+/* Evil macros !! */
+#define skip_while(cond) { for (;*cur_char && (cond);cur_char++) ; }
+#define find_eol { for (;*cur_char && *cur_char!='\n';cur_char++) ; }
+#define find_quote { for (;*cur_char && *cur_char!='\"';cur_char++) ; }
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/* Prototypes */
+
+actions_t No_Actions(
+ void );
+
+#endif /* ROBODOC_ROBODOC_H */
+
--- /dev/null
+/****h* ROBODoc/ROBODoc Cascading Style Sheet
+ * FUNCTION
+ * This is the default cascading style sheet for documentation
+ * generated with ROBODoc.
+ * You can edit this file to your own liking and then use
+ * it with the option
+ * --css <filename>
+ *
+ * This style-sheet defines the following layout
+ * +----------------------------------------+
+ * | logo |
+ * +----------------------------------------+
+ * | extra |
+ * +----------------------------------------+
+ * | | navi- |
+ * | | gation |
+ * | content | |
+ * | | |
+ * +----------------------------------------+
+ * | footer |
+ * +----------------------------------------+
+ *
+ * This style-sheet is based on a style-sheet that was automatically
+ * generated with the Strange Banana stylesheet generator.
+ * See http://www.strangebanana.com/generator.aspx
+ *
+ ******
+ * $Id: robodoc_basic_style.css,v 1.8 2007/05/10 14:20:58 thuffir Exp $
+ */
+
+body
+{
+ background-color: rgb(255,255,255);
+ color: rgb(98,84,55);
+ font-family: Arial, serif;
+ border-color: rgb(226,199,143);
+}
+
+pre
+{
+ font-family: monospace;
+ margin: 15px;
+ padding: 5px;
+ white-space: pre;
+ color: #000;
+}
+
+pre.source
+{
+ background-color: #ffe;
+ border: dashed #aa9 1px;
+}
+
+p
+{
+ margin:15px;
+}
+
+p.item_name
+{
+ font-weight: bolder;
+ margin:5px;
+ font-size: 120%;
+}
+
+#content
+{
+ font-size: 100%;
+ color: rgb(0,0,0);
+ background-color: rgb(255,255,255);
+ border-left-width: 0px;
+ border-right-width: 0px;
+ border-top-width: 0px;
+ border-bottom-width: 0px;
+ border-left-style: none;
+ border-right-style: none;
+ border-top-style: none;
+ border-bottom-style: none;
+ padding: 40px 31px 14px 17px;
+ border-color: rgb(0,0,0);
+ text-align: justify;
+}
+
+#navigation
+{
+ background-color: rgb(98,84,55);
+ color: rgb(230,221,202);
+ font-family: "Times New Roman", serif;
+ font-style: normal;
+ border-color: rgb(0,0,0);
+}
+
+a.menuitem
+{
+ font-size: 120%;
+ background-color: rgb(0,0,0);
+ color: rgb(195,165,100);
+ font-variant: normal;
+ text-transform: none;
+ font-weight: normal;
+ padding: 1px 8px 3px 1px;
+ margin-left: 5px;
+ margin-right: 5px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ border-color: rgb(159,126,57);
+ text-align: right;
+}
+
+#logo, #logo a
+{
+ font-size: 130%;
+ background-color: rgb(198,178,135);
+ color: rgb(98,84,55);
+ font-family: Georgia, serif;
+ font-style: normal;
+ font-variant: normal;
+ text-transform: none;
+ font-weight: bold;
+ padding: 20px 18px 20px 18px;
+ border-color: rgb(255,255,255);
+ text-align: right;
+}
+
+#extra, #extra a
+{
+ font-size: 128%;
+ background-color: rgb(0,0,0);
+ color: rgb(230,221,202);
+ font-style: normal;
+ font-variant: normal;
+ text-transform: none;
+ font-weight: normal;
+ border-left-width: 0px;
+ border-right-width: 0px;
+ border-top-width: 0px;
+ border-bottom-width: 0px;
+ border-left-style: none;
+ border-right-style: none;
+ border-top-style: none;
+ border-bottom-style: none;
+ padding: 12px 12px 12px 12px;
+ border-color: rgb(195,165,100);
+ text-align: center;
+}
+
+#content a
+{
+ color: rgb(159,126,57);
+ text-decoration: none;
+}
+
+#content a:hover, #content a:active
+{
+ color: rgb(255,255,255);
+ background-color: rgb(159,126,57);
+}
+
+a.indexitem
+{
+ display: block;
+}
+
+h1, h2, h3, h4, h5, h6
+{
+ background-color: rgb(221,221,221);
+ font-family: Arial, serif;
+ font-style: normal;
+ font-variant: normal;
+ text-transform: none;
+ font-weight: normal;
+}
+
+h1
+{
+ font-size: 151%;
+}
+
+h2
+{
+ font-size: 142%;
+}
+
+h3
+{
+ font-size: 133%;
+}
+
+h4
+{
+ font-size: 124%;
+}
+
+h5
+{
+ font-size: 115%;
+}
+
+h6
+{
+ font-size: 106%;
+}
+
+#navigation a
+{
+ text-decoration: none;
+}
+
+.menuitem:hover
+{
+ background-color: rgb(195,165,100);
+ color: rgb(0,0,0);
+}
+
+#extra a
+{
+ text-decoration: none;
+}
+
+#logo a
+{
+ text-decoration: none;
+}
+
+#extra a:hover
+{
+}
+
+/* layout */
+#navigation
+{
+ width: 22%;
+ position: relative;
+ top: 0;
+ right: 0;
+ float: right;
+ text-align: center;
+ margin-left: 10px;
+}
+
+.menuitem {width: auto;}
+#content {width: auto;}
+.menuitem {display: block;}
+
+
+div#footer
+{
+ background-color: rgb(198,178,135);
+ color: rgb(98,84,55);
+ clear: left;
+ width: 100%;
+ font-size: 71%;
+}
+
+div#footer a
+{
+ background-color: rgb(198,178,135);
+ color: rgb(98,84,55);
+}
+
+div#footer p
+{
+ margin:0;
+ padding:5px 10px
+}
+
+span.keyword
+{
+ color: #00F;
+}
+
+span.comment
+{
+ color: #080;
+}
+
+span.quote
+{
+ color: #F00;
+}
+
+span.squote
+{
+ color: #F0F;
+}
+
+span.sign
+{
+ color: #008B8B;
+}
+
+
+@media print
+{
+ #navigation {display: none;}
+ #content {padding: 0px;}
+ #content a {text-decoration: underline;}
+}
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/* $Id: robohdrs.c,v 1.14 2007/07/10 19:13:52 gumpu Exp $ */
+
+/****h* Docuwala/ROBOhdrs
+* NAME
+* robohdrs
+* DESCRIPTION
+* Standalone program to insert ROBODoc headers to source code files.
+* This program processes one source file at the time. Existing
+* ROBODoc headers, if any, are not checked for. Beware since this
+* may result in double headers. Current working directory should
+* be the same as where the source file is located.
+* USES
+* Exuberant Ctags 5.3.1 or newer required
+* USAGE
+* robohdrs [options] <source file>
+* EXAMPLE
+* robohdrs -p myproj test1.c
+* robohdrs -s -p myproj -i "MODIFICATION HISTORY" -i IDEAS test2.c
+*
+* Type `robohdrs -h' to see all command line options.
+* TODO
+* - garbage collection
+* - support for other languages which ctags program supports
+* SEE ALSO
+* ROBODoc https://sourceforge.net/projects/robodoc/
+* Exuberant Ctags http://ctags.sourceforge.net/
+* COPYRIGHT
+* (c) 2003 Frans Slothouber and Petteri Kettunen
+* Copying policy: GPL
+* HISTORY
+* 2003-08-08/petterik: #define `d' header (bug rep. from Anand Dhanakshirur)
+* 2003-08-04/petterik: -t option (suggestion from Anand Dhanakshirur)
+* 2003-02-21/petterik: -l option, script option tested
+*******
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+#ifndef HAVE_FORK
+
+int main( int argc, char** argv )
+{
+ printf( "Not supported on this platform\n" );
+ printf( "This program requires fork()\n" );
+ return EXIT_SUCCESS;
+}
+
+#else
+
+
+
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "headers.h"
+
+/****v* ROBOhdrs/PROGNAME
+* NAME
+* PROGNAME
+* SOURCE
+*/
+#define PROGNAME "robohdrs"
+/********** PROGNAME */
+/****v* ROBOhdrs/PROGVERSION
+* NAME
+* PROGVERSION
+* SOURCE
+*/
+#define PROGVERSION "0.01"
+/********** PROGVERSION */
+/****v* ROBOhdrs/MAXLINE
+* NAME
+* MAXLINE
+* SOURCE
+*/
+#define MAXLINE 10240
+/********** MAXLINE */
+/****v* ROBOhdrs/MAXNAME
+* NAME
+* MAXNAME
+* SOURCE
+*/
+#define MAXNAME 1024
+
+/********** MAXNAME */
+
+/****v* ROBOhdrs/ctagsBin
+* NAME
+* ctagsBin
+* SOURCE
+*/
+static char ctagsBin[MAXNAME];
+
+/********** ctagsBin */
+
+/****v* ROBOhdrs/srcSta
+* NAME
+* srcSta
+* SEE ALSO
+* src_constants
+* SOURCE
+*/
+
+static short srcSta = SRC_C;
+static short srcRem = SRC_R_C;
+static short srcEnd = SRC_E_C;
+
+/********** srcSta */
+
+typedef struct _ctag_t
+{
+ void *prev;
+ void *next;
+ char fname[MAXNAME];
+ char name[MAXNAME];
+ char decl[MAXLINE];
+ char type[MAXNAME];
+ int linenum;
+}
+ctag_t;
+
+typedef struct _custhdr_t
+{
+ void *next;
+ char name[MAXNAME];
+}
+custhdr_t;
+
+typedef struct _ctags_t
+{
+ ctag_t *ctag;
+ int cnt;
+}
+ctags_t;
+
+/****v* ROBOhdrs/myctags
+* NAME
+* myctags
+* SYNOPSIS
+* static ctags_t myctags;
+* SOURCE
+*/
+static ctags_t myctags;
+
+/********** myctags */
+/****v* ROBOhdrs/ctags
+* NAME
+* ctags
+* SYNOPSIS
+* static ctags_t *ctags;
+* SOURCE
+*/
+static ctags_t *ctags;
+
+/********** ctags */
+/****v* ROBOhdrs/custhdrs
+* NAME
+* custhdrs
+* SYNOPSIS
+* static custhdr_t *custhdrs = 0;
+* SOURCE
+*/
+static custhdr_t *custhdrs = 0;
+
+/********** custhdrs */
+/****v* ROBOhdrs/projName
+* NAME
+* projName
+* SYNOPSIS
+* static char projName[MAXNAME];
+* SOURCE
+*/
+static char projName[MAXNAME];
+
+/********** projName */
+/****v* ROBOhdrs/vcTag
+* NAME
+* vcTag
+* DESCRIPTION
+* Version control tag string. This is always specified by the user.
+* SYNOPSIS
+* static char vcTag[MAXNAME];
+* SOURCE
+*/
+static char vcTag[MAXNAME];
+
+/********** projName */
+/****v* ROBOhdrs/incSrc
+* NAME
+* incSrc
+* SYNOPSIS
+* static char incSrc = 0;
+* SOURCE
+*/
+static char incSrc = 0;
+
+/********** incSrc */
+/****f* ROBOhdrs/usage
+* NAME
+* usage
+* SYNOPSIS
+* static void usage(void)
+* SOURCE
+*/
+static void
+usage( void )
+{
+ printf( "%s version %s, robodoc header insertor\n", PROGNAME,
+ PROGVERSION );
+ printf( "(c) 2003 Frans Slothouber and Petteri Kettunen\n" );
+ printf( "%s comes with ABSOLUTELY NO WARRANTY.\n", PROGNAME );
+ printf
+ ( "This is free software, and you are welcome to redistribute it\n" );
+ printf( "under the GNU GENERAL PUBLIC LICENSE terms and conditions.\n" );
+ printf( "usage: %s [options] <source file>\n", PROGNAME );
+ printf( "Options are as follows:\n" );
+ printf( " -h show this help text\n" );
+ printf
+ ( " -i specify header item (repeat to include multiple items)\n" );
+ printf( " -l specify source code language (default C/C++)\n" );
+ printf
+ ( " Supported options are: fortran, fortran90, script, and tex.\n" );
+ printf( " -p specify project name\n" );
+ printf( " -s include SOURCE item\n" );
+ printf( " -t specify CVS/RCS tag to be inserted into a file\n" );
+ printf( " -x specify path to ctags binary\n" );
+ printf( "NOTE: requires Exuberant Ctags 5.3.1 (or newer)\n" );
+ printf( "EXAMPLES:\n" );
+ printf
+ ( "robohdrs -s -p myproj -i \"MODIFICATION HISTORY\" -i IDEAS test.c\n" );
+ printf
+ ( "robohdrs -s -p myproj -l script -t '%cHeader:%c' test.tcl\n", '$', '$' );
+ exit( 1 );
+}
+
+/********** usage */
+
+/****f* ROBOhdrs/cmdLine
+* NAME
+* cmdLine
+* SYNOPSIS
+* static void cmdLine(int argc, char **argv)
+* SOURCE
+*/
+static void
+cmdLine( int argc, char **argv )
+{
+ int ch;
+ custhdr_t *c, *nc;
+
+ while ( ( ch = getopt( argc, argv, "i:l:p:st:x:" ) ) != -1 )
+ switch ( ch )
+ {
+
+ case 's':
+ /* include source item */
+ incSrc = 1;
+ break;
+
+ case 't':
+ /* specify version control tag */
+ strncpy( vcTag, optarg, MAXNAME );
+ break;
+
+ case 'p':
+ /* specify project name */
+ strncpy( projName, optarg, MAXNAME );
+ break;
+
+ case 'i':
+ if ( !custhdrs )
+ {
+ assert( ( custhdrs =
+ ( custhdr_t * ) malloc( sizeof( custhdr_t ) ) ) );
+ custhdrs->next = 0;
+ c = custhdrs;
+ }
+ else
+ {
+ c = custhdrs;
+ while ( c->next )
+ {
+ c = c->next;
+ }
+ assert( ( nc =
+ ( custhdr_t * ) malloc( sizeof( custhdr_t ) ) ) );
+ nc->next = 0;
+ c = c->next = nc;
+ }
+ strncpy( c->name, optarg, MAXNAME );
+ break;
+
+ case 'l':
+ if ( optarg[0] == 's' && strcmp( optarg, "script" ) == 0 )
+ {
+ srcSta = SRC_SCRIPT;
+ srcRem = SRC_R_SCRIPT;
+ srcEnd = SRC_E_SCRIPT;
+ }
+ else if ( optarg[0] == 'f' && strcmp( optarg, "fortran" ) == 0 )
+ {
+ srcSta = SRC_FORTRAN;
+ srcRem = SRC_R_FORTRAN;
+ srcEnd = SRC_E_FORTRAN;
+ }
+ else if ( optarg[0] == 'f' && strcmp( optarg, "fortran90" ) == 0 )
+ {
+ srcSta = SRC_F90;
+ srcRem = SRC_R_F90;
+ srcEnd = SRC_E_F90;
+ }
+ else if ( optarg[0] == 't' && strcmp( optarg, "tex" ) == 0 )
+ {
+ srcSta = SRC_TEX;
+ srcRem = SRC_R_TEX;
+ srcEnd = SRC_E_TEX;
+ }
+ else if ( optarg[0] == 'a' && strcmp( optarg, "acm" ) == 0 )
+ {
+ srcSta = SRC_ACM;
+ srcRem = SRC_R_ACM;
+ srcEnd = SRC_E_ACM;
+ }
+ else
+ {
+ usage( );
+ }
+ break;
+
+ case 'x':
+ strncpy( ctagsBin, optarg, MAXNAME );
+ break;
+
+ case '?':
+ case 'h':
+ default:
+ usage( );
+ break;
+ }
+}
+
+/********** cmdLine */
+/****f* ROBOhdrs/linenumCompare
+* NAME
+* linenumCompare
+* SYNOPSIS
+* static int linenumCompare(void const * a, void const * b)
+* SOURCE
+*/
+static int
+linenumCompare( void const *a, void const *b )
+{
+ ctag_t *ea = ( ctag_t * ) a, *eb = ( ctag_t * ) b;
+
+ return ( ea->linenum - eb->linenum );
+}
+
+/********** linenumCompare */
+/****f* ROBOhdrs/arrangeCtags
+* NAME
+* arrangeCtags
+* SYNOPSIS
+* static void arrangeCtags(ctags_t *e)
+* SOURCE
+*/
+static void
+arrangeCtags( ctags_t * e )
+{
+ ctag_t *tmp, *ctag = e->ctag, *ep;
+
+ assert( e && e->cnt && e->ctag );
+ tmp = ( ctag_t * ) malloc( e->cnt * sizeof( ctag_t ) );
+ assert( tmp );
+
+ for ( ep = tmp;; )
+ {
+ memcpy( ep++, ctag, sizeof( ctag_t ) );
+ if ( ctag->next )
+ {
+ ctag = ctag->next;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ qsort( tmp, ( size_t ) ( e->cnt ), sizeof( ctag_t ), linenumCompare );
+ /* TODO: free ctag */
+ e->ctag = tmp;
+}
+
+/********** arrangeCtags */
+/****f* ROBOhdrs/typeOk
+* NAME
+* typeOk
+* SYNOPSIS
+* static int typeOk(char *t)
+* SOURCE
+*/
+static int
+typeOk( char *t )
+{
+ /* return 1 if supported type for headers */
+ if ( t[0] == 'f' && strncmp( t, "function", 8 ) == 0 )
+ {
+ return 1;
+ }
+ else if ( t[0] == 'v' && strncmp( t, "variable", 8 ) == 0 )
+ {
+ return 1; /* global variable */
+ }
+#if 0
+ else if ( t[0] == 's' && strncmp( t, "struct", 6 ) == 0 )
+ {
+ return 1;
+ }
+#endif
+ else if ( t[0] == 'm' && strncmp( t, "macro", 5 ) == 0 )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+/********** typeOk */
+/****f* ROBOhdrs/initMe
+* NAME
+* initMe
+* SYNOPSIS
+* static void initMe(void)
+* SOURCE
+*/
+static void
+initMe( void )
+{
+ ctags = &myctags;
+ memset( ctags, 0, sizeof( ctags_t ) );
+ projName[0] = '\0';
+ ctagsBin[0] = '\0';
+ vcTag[0] = '\0';
+}
+
+/********** initMe */
+
+/****f* ROBOhdrs/parseCtagsXLine
+* NAME
+* parseCtagsXLine
+* SYNOPSIS
+* static int parseCtagsXLine(char *buf, char *fname, char *name, char *decl, char *type, int *linenum)
+* SOURCE
+*/
+static int
+parseCtagsXLine( char *buf, char *fname, char *name, char *decl, char *type,
+ int *linenum )
+{
+ char *t, *s;
+
+ /* ctags -x output is: */
+ /* usage function 56 test.c void usage(void) */
+ sscanf( buf, "%s%s%d%s", name, type, linenum, fname );
+ s = strstr( buf, fname );
+ while ( *s++ != ' ' )
+ {
+ }
+ while ( *s == ' ' )
+ {
+ ++s;
+ }
+ t = decl;
+ while ( ( *t = *s++ ) != '\n' )
+ {
+ ++t;
+ }
+ *t = '\0';
+
+ return 0;
+}
+
+/********** parseCtagsXLine */
+
+/****f* ROBOhdrs/addList
+* NAME
+* addList
+* SYNOPSIS
+* static void addList(ctags_t *e, char *fname, char *name, char *decl, char *type, int linenum)
+* SOURCE
+*/
+static void
+addList( ctags_t * e, char *fname, char *name, char *decl, char *type,
+ int linenum )
+{
+ ctag_t *newctag, *ctag = e->ctag;
+
+ if ( !ctag )
+ {
+ /* empty list */
+ ctag = ( ctag_t * ) malloc( sizeof( ctag_t ) );
+ assert( ctag );
+ memset( ctag, 0, sizeof( ctag_t ) );
+ e->ctag = ctag;
+ }
+ else
+ {
+ while ( ctag->next )
+ {
+ ctag = ctag->next;
+ }
+ newctag = ( ctag_t * ) malloc( sizeof( ctag_t ) );
+ assert( newctag );
+ memset( newctag, 0, sizeof( ctag_t ) );
+ ctag->next = newctag;
+ newctag->prev = ctag;
+ ctag = newctag;
+ }
+
+ e->cnt++;
+
+ strncpy( ctag->fname, fname, MAXNAME );
+ strncpy( ctag->name, name, MAXNAME );
+ strncpy( ctag->decl, decl, MAXLINE );
+ strncpy( ctag->type, type, MAXNAME );
+ ctag->linenum = linenum;
+}
+
+/********** addList */
+/****f* ROBOhdrs/parseCtagsX
+* NAME
+* parseCtagsX
+* SYNOPSIS
+* static int parseCtagsX(FILE *fp)
+* SOURCE
+*/
+static int
+parseCtagsX( FILE * fp )
+{
+ char buf[MAXLINE + 1];
+ int lnum = 0, tagsParsed = 0;
+
+ while ( fgets( buf, MAXLINE, fp ) != NULL )
+ {
+ char decl[MAXNAME + 1], name[MAXNAME + 1];
+ char fname[MAXNAME + 1], type[MAXNAME + 1];
+ int linenum;
+
+ lnum++;
+ /* extract info from a line */
+ if ( parseCtagsXLine( buf, fname, name, decl, type, &linenum ) )
+ {
+ printf( "error parsing line (%d)", lnum );
+ }
+ else
+ {
+ addList( ctags, fname, name, decl, type, linenum );
+ tagsParsed++;
+ }
+ } /* end while() */
+ fclose( fp );
+
+ return tagsParsed;
+}
+
+/********** parseCtagsX */
+
+/****f* ROBOhdrs/roboFileHeader
+* NAME
+* roboFileHeader
+* FUNCTION
+* Insert source file header.
+* SYNOPSIS
+* static void roboFileHeader(FILE *fp, char *proj, char *fname)
+* SOURCE
+*/
+static void
+roboFileHeader( FILE * fp, char *proj, char *fname )
+{
+ char *s;
+
+ s = remark_markers[srcRem];
+ fprintf( fp, "%sh* %s/%s\n", header_markers[srcSta],
+ ( proj[0] ? proj : fname ), fname );
+ fprintf( fp, "%s NAME\n", s );
+ fprintf( fp, "%s %s\n", s, fname );
+ if ( *vcTag )
+ {
+ fprintf( fp, "%s %s\n", s, vcTag );
+ }
+ fprintf( fp, "%s DESCRIPTION\n", s );
+ fprintf( fp, "%s*******%s\n", s,
+ ( end_remark_markers[srcEnd] ? end_remark_markers[srcEnd] :
+ "" ) );
+}
+
+/********** roboFileHeader */
+
+/****f* ROBOhdrs/roboHeader
+* NAME
+* roboHeader
+* SYNOPSIS
+* static void roboHeader(FILE *fp, char *fname, char *name, char *type, char *decl)
+* SOURCE
+*/
+static void
+roboHeader( FILE * fp, char *fname, char *name, char *type, char *decl )
+{
+ custhdr_t *c;
+ char *s;
+
+ s = remark_markers[srcRem];
+ if ( type[0] == 'v' )
+ {
+ fprintf( fp, "%sv* %s/%s\n", header_markers[srcSta], fname, name );
+ }
+ else if ( type[0] == 'm' )
+ {
+ fprintf( fp, "%sd* %s/%s\n", header_markers[srcSta], fname, name );
+ }
+#if 0
+ else if ( type[0] == 's' )
+ {
+ fprintf( fp, "/****s* %s/%s\n", fname, name );
+ }
+#endif
+ else
+ {
+ fprintf( fp, "%sf* %s/%s\n", header_markers[srcSta], fname, name );
+ }
+
+ fprintf( fp, "%s NAME\n%s %s\n", s, s, name );
+ if ( type[0] != 'm' )
+ {
+ fprintf( fp, "%s SYNOPSIS\n%s %s\n", s, s, decl );
+ }
+ if ( custhdrs )
+ {
+ for ( c = custhdrs;; )
+ {
+ fprintf( fp, "%s %s\n", s, c->name );
+ if ( c->next )
+ {
+ c = c->next;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if ( incSrc )
+ {
+ fprintf( fp, "%s SOURCE\n%s\n", s,
+ ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
+ s ) );
+ }
+ else
+ {
+ fprintf( fp, "%s***%s\n", s,
+ ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
+ "" ) );
+ }
+}
+
+/********** roboHeader */
+
+/****f* ROBOhdrs/insertSrcEnd
+* NAME
+* insertSrcEnd
+* SYNOPSIS
+* static void insertSrcEnd(FILE *fp, char *funcname)
+* SOURCE
+*/
+
+static void
+insertSrcEnd( FILE * fp, char *funcname )
+{
+ fprintf( fp, "%s********* %s %s\n", end_markers[srcEnd], funcname,
+ ( end_remark_markers[srcSta] ? end_remark_markers[srcSta] :
+ "" ) );
+}
+
+/********** insertSrcEnd */
+
+/****f* ROBOhdrs/insertHeaders
+* NAME
+* insertHeaders
+* SYNOPSIS
+* static void insertHeaders(ctags_t *e, char *project, char *dstpath, char *srcpath)
+* SOURCE
+*/
+static void
+insertHeaders( ctags_t * e, char *project, char *dstpath, char *srcpath )
+{
+ FILE *ifp, *ofp;
+ ctag_t *ctag = e->ctag;
+ int lnum = 0, funcline = 0;
+ char buf[MAXLINE], *funcname = 0;
+
+ if ( !ctag || !dstpath || !srcpath )
+ {
+ return;
+ }
+
+ assert( ofp = fopen( dstpath, "w" ) );
+ assert( ifp = fopen( srcpath, "r" ) );
+
+ /* include file header only if project name is defined */
+ if ( project )
+ {
+ roboFileHeader( ofp, project, dstpath );
+ }
+
+ while ( fgets( buf, MAXLINE, ifp ) != NULL )
+ {
+ lnum++;
+ while ( ctag->prev )
+ {
+ ctag = ctag->prev;
+ }
+ for ( ;; )
+ {
+ if ( incSrc && funcline && lnum >= funcline
+ && ctag->linenum == lnum )
+ {
+ funcline = 0;
+ insertSrcEnd( ofp, funcname );
+ }
+ if ( ctag->linenum == lnum )
+ {
+ if ( typeOk( ctag->type ) )
+ {
+ roboHeader( ofp, ctag->fname, ctag->name, ctag->type,
+ ctag->decl );
+ funcline = lnum;
+ funcname = ctag->name;
+ }
+ break;
+ }
+ else if ( ctag->next )
+ {
+ ctag = ctag->next;
+ }
+ else
+ {
+ break;
+ }
+ } /* end ctag loop */
+ fprintf( ofp, "%s", buf );
+ }
+
+ if ( incSrc && funcline )
+ {
+ insertSrcEnd( ofp, funcname );
+ }
+
+ fclose( ifp );
+ fclose( ofp );
+}
+
+/********** insertHeaders */
+/****f* ROBOhdrs/doCtagsExec
+* NAME
+* doCtagsExec
+* SYNOPSIS
+* static FILE * doCtagsExec(char *fname)
+* SOURCE
+*/
+static FILE *
+doCtagsExec( char *fname )
+{
+ int fd[2], pid;
+ FILE *incoming;
+ char *mybin, *bin = "ctags";
+
+ mybin = ( ctagsBin[0] ? ctagsBin : bin );
+
+ if ( pipe( fd ) == -1 )
+ {
+ fprintf( stderr, "pipe failed\n" );
+ exit( 1 );
+ }
+
+ if ( ( pid = fork( ) ) == 0 )
+ {
+ close( 1 );
+ dup( fd[1] );
+ close( fd[0] );
+ if ( execlp( mybin, mybin, "-x", fname, NULL ) == -1 )
+ {
+ fprintf( stderr, "execlp failed\n" );
+ exit( 1 );
+ }
+ }
+ else if ( pid == -1 )
+ {
+ fprintf( stderr, "fork failed\n" );
+ exit( 1 );
+ }
+ else
+ {
+ close( 0 );
+ dup( fd[0] );
+ close( fd[1] );
+
+ if ( ( incoming = fdopen( 0, "r" ) ) == NULL )
+ {
+ fprintf( stderr, "fdopen failed\n" );
+ exit( 1 );
+ }
+ }
+
+ return incoming;
+}
+
+/********** doCtagsExec */
+/****f* ROBOhdrs/doFile
+* NAME
+* doFile
+* SYNOPSIS
+* static void doFile(char *proj, char *fname)
+* SOURCE
+*/
+static void
+doFile( char *proj, char *fname )
+{
+ char buf[MAXLINE];
+
+ /* backup */
+ sprintf( buf, "/bin/cp -p %s %s~", fname, fname );
+ system( buf );
+
+ if ( parseCtagsX( doCtagsExec( fname ) ) < 1 )
+ {
+ fprintf( stderr, "no tags\n" );
+ exit( 1 );
+ }
+
+ arrangeCtags( ctags );
+ sprintf( buf, "%s~", fname );
+ insertHeaders( ctags, proj, fname, buf );
+}
+
+/********** doFile */
+/****f* ROBOhdrs/cleanUp
+* NAME
+* cleanUp
+* SYNOPSIS
+* static void cleanUp(void)
+* SOURCE
+*/
+static void
+cleanUp( void )
+{
+
+}
+
+/********** cleanUp */
+
+
+/****f* ROBOhdrs/main
+* NAME
+* main
+* SYNOPSIS
+* int main(int argc, char **argv)
+* SOURCE
+*/
+int
+main( int argc, char **argv )
+{
+ initMe( );
+ cmdLine( argc, argv );
+ argc -= optind;
+ argv += optind;
+ if ( argc == 1 )
+ {
+ doFile( projName, argv[0] );
+ }
+ else
+ {
+ usage( );
+ }
+ cleanUp( );
+ return 0;
+}
+
+/********** main */
+
+
+#endif /* HAVE_FORK */
+
--- /dev/null
+// vi: spell ff=unix
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/RTF_Generator
+ * FUNCTION
+ * A collection of functions to generate output in RTF format.
+ * Supports sections upto 7 levels deep.
+ * TODO
+ * Documentation
+ * MODIFICATION HISTORY
+ * ????-??-?? Anthon Pang V1.0
+ * 2003-02-03 Frans Slothouber Refactoring
+ * Anthon Pang
+ *****
+ * $Id: rtf_generator.c,v 1.27 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rtf_generator.h"
+#include "util.h"
+#include "robodoc.h"
+#include "globals.h"
+#include "headers.h"
+#include "generator.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+void RB_RTF_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ /* Find out how to emphasize this in rtf */
+ fprintf( dest_doc, "%s", name );
+}
+
+void RB_RTF_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count )
+{
+ int i;
+
+ for ( i = 0; i < count; ++i )
+ {
+ struct RB_header *cur_header = headers[i];
+
+ if ( cur_header->name && cur_header->function_name )
+ {
+ fprintf( dest_doc, "{" );
+ RB_RTF_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "}{\\v " );
+ RB_RTF_Generate_String( dest_doc, cur_header->function_name );
+ fprintf( dest_doc, "}\\line\n" );
+ }
+ }
+}
+
+void RB_RTF_Generate_Label(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+ /* Empty */
+}
+
+void RB_RTF_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "\\par \\fs18 %s ", name );
+}
+
+char *RB_RTF_Get_Default_Extension(
+ void )
+{
+ return ".rtf";
+}
+
+
+void RB_RTF_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ fprintf( dest_doc, "\\par {\\s%d %s \\par} \\pard\\plain\n", depth, name );
+}
+
+void RB_RTF_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( dest_doc );
+ USE( depth );
+ USE( name );
+ /* empty */
+}
+
+/*x**f* RTF_Generator/RB_RTF_Generate_Doc_Start
+ * NAME
+ * RB_RTF_Generate_Doc_Start --
+ ******
+ */
+
+void RB_RTF_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc )
+{
+ USE( toc );
+
+ /* RTF header */
+ fprintf( dest_doc, "{\\rtf1\\ansi \\deff0"
+ "{\\fonttbl;"
+ "\\f0\\fswiss MS Sans Serif;"
+ "\\f1\\fmodern Courier New;"
+ "\\f2\\ftech Symbol;"
+ "}"
+ "{\\colortbl;"
+ "\\red255\\green255\\blue255;"
+ "\\red0\\green0\\blue0;" "\\red0\\green0\\blue255;" "}" );
+
+ /* RTF document info */
+ fprintf( dest_doc, "{\\info"
+ "{\\title %s}" "{\\comment\n" " Source: %s\n", name, src_name );
+ if ( course_of_action.do_nogenwith )
+ {
+ fprintf( dest_doc, " " "}" "}" );
+ }
+ else
+ {
+ fprintf( dest_doc, " " COMMENT_ROBODOC " " "}" "}" );
+ }
+
+ /* RTF stylesheet */
+ fprintf( dest_doc,
+ "{\\stylesheet\n"
+ "{\\s1 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel0\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 1;}\n"
+ "{\\s2 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel1\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 2;}\n"
+ "{\\s3 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel2\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 3;}\n"
+ "{\\s4 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel3\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 4;}\n"
+ "{\\s5 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel4\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 5;}\n"
+ "{\\s6 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel5\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 6;}\n"
+ "{\\s7 \\fs20 \\keepn\\widctlpar\\jclisttab \\outlinelevel6\\adjustright \\cgrid \\sbasedon0 \\snext0 heading 7;}}\n" );
+ /* RTF document format */
+ fprintf( dest_doc, "{\\margl1440\\margr1440}\\fs18\n" );
+
+ /* RTF document section */
+ fprintf( dest_doc, "\\f0\\cb1\\cf3\\b1\\qc"
+ "{\\super #{\\footnote{\\super #}%s_TOC}}"
+ "{\\super ${\\footnote{\\super $}Contents}}"
+ "{TABLE OF CONTENTS}\\ql\\b0\\fs20\\cf2\\par\n", src_name );
+}
+
+/*x**f* RTF_Generator/RB_RTF_Generate_Doc_End
+ * NAME
+ * RB_RTF_Generate_Doc_End --
+ ******
+ */
+
+void RB_RTF_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( name );
+
+ fputc( '}', dest_doc );
+}
+
+/*x**f* RTF_Generator/RB_RTF_Generate_Header_Start
+ * NAME
+ * RB_RTF_Generate_Header_Start --
+ ******
+ */
+
+void RB_RTF_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ if ( cur_header->name && cur_header->function_name )
+ {
+ RB_RTF_Generate_String( dest_doc, cur_header->function_name );
+ fprintf( dest_doc, "\\page {\\super #{\\footnote{\\super #}" );
+ RB_RTF_Generate_String( dest_doc, cur_header->function_name );
+ fprintf( dest_doc, "}}{\\super ${\\footnote{\\super $}" );
+ RB_RTF_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "}} " );
+ RB_RTF_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "\\line\n" );
+ }
+}
+
+/*x**f* RTF_Generator/RB_RTF_Generate_Header_End
+ * NAME
+ * RB_RTF_Generate_Header_End --
+ ******
+ */
+
+void RB_RTF_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+
+ fprintf( dest_doc, "\\par\n" );
+}
+
+
+void RB_RTF_Generate_Item_Begin(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\\line {\\f1{} \\fs18" );
+}
+
+void RB_RTF_Generate_Item_End(
+ FILE *dest_doc )
+{
+ fputc( '}', dest_doc );
+ fputc( '\n', dest_doc );
+}
+
+/*x**f* RTF_Generator/RB_RTF_Generate_Empty_Item
+ * NAME
+ * RB_RTF_Generate_Empty_Item --
+ ******
+ */
+
+void RB_RTF_Generate_Empty_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "\n" );
+}
+
+
+void RB_RTF_Generate_Link(
+ FILE *dest_doc,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname
+ )
+{
+ USE( dest_name );
+ USE( filename );
+
+ if ( strcmp( labelname, linkname ) )
+ {
+ fprintf( dest_doc, "{\\b " );
+ RB_RTF_Generate_String( dest_doc, linkname );
+ fprintf( dest_doc, "}{\\v " );
+ RB_RTF_Generate_String( dest_doc, labelname );
+ fprintf( dest_doc, "}" );
+ }
+ else
+ {
+ RB_RTF_Generate_String( dest_doc, labelname );
+ }
+}
+
+void RB_RTF_Generate_String(
+ FILE *dest_doc,
+ char *a_string )
+{
+ int i;
+ int l = strlen( a_string );
+ unsigned char c;
+
+ for ( i = 0; i < l; ++i )
+ {
+ c = a_string[i];
+ RB_RTF_Generate_Char( dest_doc, c );
+ }
+}
+
+void RB_RTF_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ switch ( c )
+ {
+ case '\n':
+ assert( 0 );
+ break;
+ case '\t':
+ assert( 0 );
+ break;
+ case '\\':
+ case '{':
+ case '}':
+ fputc( '\\', dest_doc );
+ fputc( c, dest_doc );
+ break;
+ default:
+ fputc( c, dest_doc );
+ }
+}
--- /dev/null
+#ifndef ROBODOC_RTF_GENERATOR_H
+#define ROBODOC_RTF_GENERATOR_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+
+void RB_RTF_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count );
+void RB_RTF_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc );
+void RB_RTF_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+void RB_RTF_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_RTF_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_RTF_Generate_Empty_Item(
+ FILE *dest_doc );
+void RB_RTF_Generate_Link(
+ FILE *dest_doc,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname );
+void RB_RTF_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_RTF_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+void RB_RTF_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+
+/* size_t RB_RTF_Get_Len_Extension(); */
+/* void RB_RTF_Add_Extension(char* name); */
+char *RB_RTF_Get_Default_Extension(
+ void );
+void RB_RTF_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+
+void RB_RTF_Generate_Item_Begin(
+ FILE *dest_doc );
+void RB_RTF_Generate_Item_End(
+ FILE *dest_doc );
+void RB_RTF_Generate_Label(
+ FILE *dest_doc,
+ char *name );
+void RB_RTF_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+void RB_RTF_Generate_String(
+ FILE *dest_doc,
+ char *a_string );
+
+#endif /* ROBODOC_RTF_GENERATOR_H */
--- /dev/null
+# vi: ff=unix spell
+#****h* ROBODoc/ROBOTestFrame
+# FUNCTION
+# A Perl module with a set of handy functions to create
+# test scripts.
+#
+# These function are:
+# * runrobo
+# * add_source
+# * add_configuration
+# * clean
+# * mkdocdir
+# * is_latex_balanced
+# * read_hexdump
+#
+#*****
+
+package ROBOTestFrame;
+require Exporter;
+ @ISA = qw(Exporter);
+ @EXPORT = qw(
+ runrobo
+ add_source
+ add_configuration
+ clean mkdocdir
+ is_latex_balanced
+ read_hexdump
+ ); # symbols to export on request
+
+use strict;
+use warnings;
+# TODO Try to get this to work without IPC::Run
+use IPC::Run 'run';
+use File::Path;
+use File::Basename;
+use IO::File;
+
+#****f* ROBOTestFrame/robo_win, robo_unix
+# FUNCTION
+# Location of the ROBODoc executable.
+# SOURCE
+#
+my $robo_win = "../robodoc.exe";
+my $robo_unix = "../robodoc";
+#****
+
+#****f* ROBOTestFrame/source_directory
+# FUNCTION
+# Name of the source directory used to test ROBODoc.
+# SOURCE
+#
+my $source_directory = "Src";
+#*****
+
+#****f* ROBOTestFrame/documentation_directory
+# FUNCTION
+# Name of the documentation directory used to test ROBODoc.
+# SOURCE
+my $documentation_directory = "Doc";
+#*****
+
+#****f* ROBOTestFrame/configuration_directory
+# FUNCTION
+# Name of the documentation directory used to test ROBODoc.
+# SOURCE
+my $configuration_directory = "Config";
+#*****
+
+#****f* ROBOTestFrame/runrobo
+# FUNCTION
+# Run robodoc with the given set of
+# arguments and capture all output to
+# stdout en stderr.
+# INPUTS
+# A list of options for robodoc.exr
+# RETURNS
+# stdout and stderr.
+# SOURCE
+#
+sub runrobo
+{
+ my $robo = '';
+ if ( $^O eq 'linux' ) {
+ $robo = $robo_unix;
+ } else {
+ $robo = $robo_win;
+ }
+ run( [ $robo, @_ ], \my( $in, $out, $err ) );
+ return ($out, $err);
+}
+#*****
+
+
+#****f* ROBOTestFrame/add_configuration
+# FUNCTION
+# Add a configuration file somewhere in Config/
+# INPUTS
+# - filepath -- path to a file.
+# - configuration -- the content for this file
+# - binary -- write the raw bytes. [optional]
+# SOURCE
+#
+sub add_configuration
+{
+ my $filepath = shift;
+ my $configuration = shift;
+ my $binary = shift;
+ add_file( $configuration_directory, $filepath, $configuration, $binary )
+}
+
+#*****
+
+#****f* ROBOTestFrame/add_source
+# FUNCTION
+# Add a single source file somewhere in Src/
+# INPUTS
+# - filepath -- path to a file.
+# - source_code -- the source code to go into this file
+# - binary -- write the raw bytes. [optional]
+# SOURCE
+#
+sub add_source
+{
+ my $filepath = shift;
+ my $source_code = shift;
+ my $binary = shift;
+
+ add_file( $source_directory, $filepath, $source_code, $binary )
+}
+
+#*****
+
+#****f* ROBOTestFrame/add_file
+# FUNCTION
+# Add a single file somewhere in base_path.
+# INPUTS
+# - base_path -- base path
+# - filepath -- relative path to a file.
+# - content -- the content to go into this file
+# - binary -- write the raw bytes. [optional]
+# SOURCE
+#
+
+sub add_file
+{
+ my $base_path = shift;
+ my $filepath = shift;
+ my $content = shift;
+ my $binary = shift;
+
+ my $path = $base_path . dirname( $filepath );
+
+ $path =~ s/\.$//; # Fix for Perl 5.8.0 under Linux.
+
+ if ( ! -e "$path" ) {
+ mkpath $path or die "can't create $path";
+ }
+
+ my $full_filepath = "$base_path/$filepath";
+ my $file = IO::File->new(">$full_filepath") or
+ die "Can't open $full_filepath";
+ if ( $binary and ( $binary eq 'binary' ) ) {
+ binmode( $file );
+ }
+ print $file $content;
+ $file->close();
+}
+#*****
+
+
+#****f* ROBOTestFrame/clean
+# FUNCTION
+# Clean source and documentation directories.
+# SOURCE
+#
+sub clean
+{
+ if ( -e $source_directory ) {
+ rmtree( $source_directory ) or die;
+ }
+ if ( -e $documentation_directory ) {
+ rmtree( $documentation_directory ) or die;
+ }
+ if ( -e $configuration_directory ) {
+ rmtree( $configuration_directory ) or die;
+ }
+}
+
+#*****
+
+#****f* ROBOTestFrame/mkdocdir
+# FUNCTION
+# Create a empty documentation directory.
+# This is handy for tests that use --singledoc.
+# SOURCE
+sub mkdocdir
+{
+ if ( ! -e $documentation_directory ) {
+ mkpath( $documentation_directory );
+ }
+}
+
+#****
+
+#****f* ROBOTestFrame/is_latex_balanced
+# FUNCTION
+# Test the balance of a latex file.
+# A latex file is balanced if every
+# /begin{xxxx}
+# is ended with a
+# /end{xxx}
+# INPUTS
+# * path - path to a latex file.
+# RETURNS
+# * 0 -- file is not balanced
+# * 1 -- file is balanced
+# SOURCE
+
+sub is_latex_balanced {
+ my $path = shift;
+ my @stack;
+ local( $/ ) ;
+ my $file = IO::File->new("<$path") or die "$path : $!";
+ my $string = <$file>;
+ $file->close();
+
+ while ( $string =~ m/(begin|end)\{([^}]+)\}/g ) {
+ my $b_e = $1; # begin or end
+ my $kind = $2; # document, or equation, or ...
+ if ( $b_e eq "begin" ) {
+ push( @stack, $kind );
+ } else {
+ if ( pop( @stack ) eq $kind ) {
+ # OK. begin and end matches.
+ } else {
+ # Not OK!
+ # begin{ something }
+ # followed by
+ # end{ something else }
+ return 0; # Failure.
+ }
+
+ }
+ }
+ if ( scalar( @stack ) ) {
+ # there are items left!
+ return 0; # Not OK.
+ }
+ return 1; # OK!
+}
+#******
+
+
+#****f* ROBOTestFrame/read_hexdump
+# FUNCTION
+# Reads a hexdump made with xxd (part of vim http://www.vim.org/)
+# This makes it possible to add files with all kinds of
+# different formats and characters.
+#
+# Storing it in hexdump format makes sure that these files are
+# not changed when they are checked into cvs or unzipped.
+#
+# This makes is possible to test cr/lf problems and internationalization
+# issues.
+#
+# INPUTS
+# * path - path to a hexdump file.
+# RETURNS
+# The decoded content of the file as a single string.
+# SOURCE
+
+sub read_hexdump {
+ my $path = shift;
+ my $file = IO::File->new("<$path") or die "$path : $!";
+
+ my $string = '';
+ my @all_bytes = ();
+ while ( my $line = <$file> ) {
+ $line =~ s/^\S+:\s//; # remove address
+ $line =~ s/\s\s+.*$//; # remove ascii
+ $line =~ s/(\S\S)(\S\S)/$1 $2/g;
+ # Now only the words are left.
+ my @data = split( /\s/, $line );
+ my @bytes = map { chr hex } @data;
+ push( @all_bytes, @bytes );
+ }
+ # TODO try a join() here.
+ foreach my $c ( @all_bytes ) {
+ $string .= $c;
+ }
+
+ $file->close();
+ return $string;
+}
+
+#******
+
+1;
--- /dev/null
+0;
+# OBSOLETE
+#
--- /dev/null
+0000000: 2a2a 2a2a 662a 2074 6573 742f 6973 6f38 ****f* test/iso8
+0000010: 3835 392d 310d 0a2a 2046 554e 4354 494f 859-1..* FUNCTIO
+0000020: 4e0d 0a2a 2054 6869 7320 6973 2061 2074 N..* This is a t
+0000030: 6573 7420 6f66 2049 534f 2d38 3835 392d est of ISO-8859-
+0000040: 3120 696e 7075 743a 20e4 f6fc 20c4 d6dc 1 input: ... ...
+0000050: 20df 20b2 b9b3 0d0a 2a0d 0a2a 2041 c420 . .....*..* A.
+0000060: 313a 2041 2072 6567 756c 6172 206c 696e 1: A regular lin
+0000070: 652c 206e 6f74 2061 2070 7265 666f 726d e, not a preform
+0000080: 6174 7465 6420 6974 656d 2c20 636f 6e74 atted item, cont
+0000090: 6169 6e69 6e67 2061 206e 6f6e 2d41 5343 aining a non-ASC
+00000a0: 4949 2063 6861 720d 0a2a 0d0a 2a20 c441 II char..*..* .A
+00000b0: 2032 3a20 4120 7072 6566 6f72 6d61 7474 2: A preformatt
+00000c0: 6564 2069 7465 6d20 2862 7567 3f29 2c20 ed item (bug?),
+00000d0: 7374 6172 7469 6e67 2077 6974 6820 6120 starting with a
+00000e0: 6e6f 6e2d 4153 4349 4920 6368 6172 0d0a non-ASCII char..
+00000f0: 2a0d 0a2a 20dc 5520 333a 2041 2072 6567 *..* .U 3: A reg
+0000100: 756c 6172 206c 696e 652c 2073 7461 7274 ular line, start
+0000110: 696e 6720 7769 7468 2061 2064 6966 6665 ing with a diffe
+0000120: 7265 6e74 206e 6f6e 2d41 5343 4949 2063 rent non-ASCII c
+0000130: 6861 720d 0a2a 0d0a 2a20 5472 616e 736c har..*..* Transl
+0000140: 6174 6520 7769 7468 3a0d 0a2a 2072 6f62 ate with:..* rob
+0000150: 6f64 6f63 202d 2d73 7263 2069 736f 3838 odoc --src iso88
+0000160: 3539 2d31 2e74 7874 202d 2d64 6f63 2069 59-1.txt --doc i
+0000170: 736f 3838 3539 2d31 646f 6320 2d2d 7369 so8859-1doc --si
+0000180: 6e67 6c65 6669 6c65 202d 2d68 746d 6c20 nglefile --html
+0000190: 2d2d 6e6f 7072 650d 0a2a 0d0a 2a20 c448 --nopre..*..* .H
+00001a0: 4e4c 4943 4845 204d 4f44 454c 4c45 0d0a NLICHE MODELLE..
+00001b0: 2a20 4669 7273 7420 6368 6172 206f 6620 * First char of
+00001c0: 7468 6973 2069 7465 6d20 6e61 6d65 2069 this item name i
+00001d0: 7320 6d69 7373 696e 670d 0a2a 0d0a 2a20 s missing..*..*
+00001e0: dc42 4552 5452 4147 4241 524b 4549 540d .BERTRAGBARKEIT.
+00001f0: 0a2a 2041 6e6f 7468 6572 2069 7465 6d20 .* Another item
+0000200: 6e61 6d65 2073 7461 7274 696e 6720 7769 name starting wi
+0000210: 7468 206e 6f6e 2d41 5343 4949 0d0a 2a2a th non-ASCII..**
+0000220: 2a2a 2a0d 0a0d 0a ***....
--- /dev/null
+0000000: 6974 656d 733a 0d0a 2020 2020 4655 4e43 items:.. FUNC
+0000010: 5449 4f4e 0d0a 2020 2020 c448 4e4c 4943 TION.. .HNLIC
+0000020: 4845 204d 4f44 454c 4c45 0d0a 2020 2020 HE MODELLE..
+0000030: dc42 4552 5452 4147 4241 524b 4549 540d .BERTRAGBARKEIT.
+0000040: 0a0d 0a ...
--- /dev/null
+0000000: 6974 656d 733a 0a20 2020 2054 4553 540a items:. TEST.
+0000010: 2020 2020 464f 4f0a 2020 2020 4241 520a FOO. BAR.
+0000020: 6974 656d 206f 7264 6572 3a0a 2020 2020 item order:.
+0000030: 4241 520a 2020 2020 464f 4f0a BAR. FOO.
--- /dev/null
+0000000: 6974 656d 733a 0d0a 2020 2020 4241 520d items:.. BAR.
+0000010: 0a20 2020 2046 4f4f 0d0a 2020 2020 534e . FOO.. SN
+0000020: 4146 550d 0a69 7465 6d20 6f72 6465 723a AFU..item order:
+0000030: 0d0a 2020 2020 4241 520d 0a .. BAR..
--- /dev/null
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+
+#****h* ROBODoc System Tests/ASCII Generator
+# FUNCTION
+# Test ROBODoc ASCII generator.
+#
+#****
+
+
+#****v* ASCII Generator/dummy_header_1
+# FUNCTION
+# A dummy header to put into dummy source files.
+# SOURCE
+#
+ my $dummy_header_1 = <<'EOF';
+C ****f* Lib/Func
+C NAME
+C Func -- useless
+C Computes the value:
+C |latex \begin{equation}
+C |latex x = 0
+C |latex \end{equation}
+C app
+C
+C SYNOPSIS
+C Func = Func (n)
+C Computes the value:
+C |latex \begin{equation}
+C |latex x = 0
+C |latex \end{equation}
+C BUGS
+C Generates screwy TeX
+C ***
+ real function Func(n)
+ Func = 0
+ end function Func
+
+EOF
+
+#****
+
+
+#****v* ASCII Generator/dummy_header_2
+# FUNCTION
+# A dummy header to put into dummy source files.
+# SOURCE
+#
+ my $dummy_header_2 = <<'EOF';
+C ****f* Lib/Func
+C NAME
+C Func -- useless
+C SYNOPSIS
+C Example:
+C Foo foo foo
+C foo
+C
+C Test paragraph.
+C Do da diddi do da dom dom.
+C
+C BUGS
+C A list test:
+C * item 1
+C * item 2
+C * item 3
+C
+C ***
+ real function Func(n)
+ Func = 0
+ end function Func
+
+EOF
+
+#****
+
+
+#****x* ASCII Generator/smoke test
+# FUNCTION
+# Try different combination of options.
+# This should not cause any asserts().
+#
+# SOURCE
+{
+ my @sources = ( \$dummy_header_1, \$dummy_header_2 );
+
+ foreach my $source_ref ( @sources ) {
+ foreach my $mode_1 qw( --sections --toc --index --nopre ) {
+ foreach my $mode_2 qw( --singledoc --multidoc ) {
+ mkdocdir();
+ add_source( "test.c", $$source_ref );
+ my ( $out, $err ) = runrobo(
+ qw(--src Src
+ --doc Doc/test
+ --ascii
+ ), $mode_1, $mode_2 );
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ my $file_name;
+ # Docments names differ for the different
+ # modes.
+ if ( $mode_2 eq "--multidoc" ) {
+ $file_name = "Doc/test/test_c.txt";
+ } else {
+ $file_name = "Doc/test.txt";
+ }
+ file_exists_ok( $file_name,
+ 'there should be documentation' );
+ clean();
+ }
+ }
+ }
+}
+#****
+
--- /dev/null
+#!perl
+# vi: spell ff=unix
+#------------------------------------------------------------------------------
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+use XML::Simple;
+
+#****h* ROBODoc System Tests/Basics
+# FUNCTION
+# Tests basic ROBODoc funcionallity. These are all
+# 'happy paths'.
+#
+#****
+
+#****v* Basics/Dummy Headers
+# FUNCTION
+# A dummy header to put into dummy source files.
+# SOURCE
+#
+my $source = <<'EOF';
+/****f* Test/test
+ * NAME
+ * Test
+ ******
+ */
+EOF
+#*****
+
+#****x* Basics/Option version
+# FUNCTION
+# Test robodoc --version, this should report the current version.
+# SOURCE
+{
+ my ( $out, $err ) = runrobo('--version');
+ like( $out, qr/\d+\.\d+.\d+/, '--version should print version number' );
+ is( $err, '', '... and no error' );
+}
+#****
+
+#****x* Basics/Option help
+# FUNCTION
+# Test the option --help.
+# SOURCE
+{
+ my ( $out, $err ) = runrobo('--help');
+ like( $out, qr/ROBODoc Version/, '--help should print version number' );
+ is( $err, '', '... and no error' );
+}
+#****
+
+
+#****x* Basics/Option multidoc
+# FUNCTION
+# Test --multidoc for html output format.
+# (Multidoc for other modes does not make much sense).
+# We create one source file with a simple header and create multidoc
+# SOURCE
+{
+ add_source( "test.c", $source );
+ my ( $out, $err ) = runrobo(qw(--src Src --doc Doc --multidoc --html));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "Doc/test_c.html", 'there should be documentation' );
+ file_exists_ok( "Doc/robodoc.css", 'and a stylesheet' );
+
+ clean();
+}
+#****
+
+
+# TODO FS
+# test --multidoc for --troff
+#
+
+# TODO FS
+# test --css
+#
+
+#****x* Basics/Singledoc Outputmode with Different Formats
+# FUNCTION
+# Test singledoc mode for all supported output formats
+# and see if it creates output in this format.
+#
+# SOURCE
+
+{
+ add_source( "test.c", $source );
+
+ my %output_modes = (
+ '--html' => 'html',
+ '--rtf' => 'rtf',
+ '--test' => 'xml',
+ '--latex' => 'tex',
+ '--dbxml' => 'xml',
+ );
+
+ foreach my $mode ( keys %output_modes ) {
+ my $file_extension = $output_modes{$mode};
+
+ my @arguments = qw(--src Src --doc testdoc --singledoc);
+ push( @arguments, $mode );
+
+ my ( $out, $err ) = runrobo(@arguments);
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "testdoc.$file_extension",
+ 'there should be documentation' );
+ if ( $mode eq "--html" ) {
+ file_exists_ok( "testdoc.css", 'and a stylesheet' );
+ }
+
+ unlink("testdoc.$file_extension") if -e "testdoc.$file_extension";
+ unlink('testdoc.css') if -e 'testdoc.css';
+
+ }
+
+ clean();
+}
+
+#****
+
+#****v* Basics/test_source_2
+# FUNCTION
+# A dummy header to put into dummy source files.
+# This one has headers at several levels.
+# SOURCE
+#
+my $test_source_2 = <<'EOF';
+/****f* Level_1/Level_2
+ * NAME
+ * Test
+ ******
+ */
+
+/****f* Level_2/_Level_3
+ * NAME
+ * Test
+ ******
+ */
+
+/****f* Level_3/_Level_4
+ * NAME
+ * Test
+ ******
+ */
+
+EOF
+#*****
+
+
+#****x* Basics/Option first section level
+# FUNCTION
+# Test --first_section_level.
+#
+# The level of the first section should be
+# at level 3 instead of 1.
+#
+# SOURCE
+{
+ add_source( "test.c", $test_source_2 );
+ mkdocdir();
+ my ( $out, $err ) = runrobo(
+ qw(--src Src
+ --doc Doc/test
+ --singledoc
+ --sections
+ --test
+ --toc
+ --first_section_level 3));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "Doc/test.xml", 'there should be documentation' );
+
+ # Now test the level of the first header.
+ my $documentation = XMLin( 'Doc/test.xml' );
+ my $section = $documentation->{'section'};
+ is ( $section->[0]->{'depth'}, '3', 'First section is at level 3' );
+ my $subsection = $section->[0]->{'section'};
+ is ( $subsection->{'depth'}, '4', 'First subsection is at level 4' );
+ is ( $section->[1]->{'depth'}, '3', 'Second section is at level 3' );
+
+ clean();
+}
+#****
+
+
+1;
+
--- /dev/null
+#!/usr/bin/perl -w
+# vi: ff=unix spell
+
+#****h* ROBODoc System Tests/Encoding
+# FUNCTION
+# Test decoding en encoding of non 7bit ASCII characters.
+#*****
+
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+use XML::Simple;
+use Data::Dumper;
+
+#
+# This tests a header with item names that start with
+# an letter with an umlaut.
+# ROBODoc currently has some problems with this.
+#
+
+{
+ my $source = read_hexdump( 'TestData/iso8859-1_german_header_c.xxd' );
+ my $config = read_hexdump( 'TestData/iso8859-1_german_robodoc_rc.xxd' );
+
+ add_source( "test.c", $source, 'binary' );
+ add_configuration( "test.rc", $config, 'binary' );
+ mkdocdir();
+ my ( $out, $err ) = runrobo(qw(
+ --src Src
+ --doc Doc/test
+ --rc Config/test.rc
+ --singledoc --test --nopre ));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "Doc/test.xml", 'there should be documentation' );
+
+ my $documentation = XMLin( 'Doc/test.xml' );
+ my $header = $documentation->{'header'};
+# is ( $header->{'name'}, 'Test Foo Bar/Name With Spaces', 'Header name' );
+ my $items = $header->{'item'};
+ print Dumper( $items );
+ my $body = $items->{'FUNCTION'}->{'item_body'};
+
+ # TODO currently a bug in robodoc.
+ is( $body->{'pre'}, undef, 'No preformatted stuff in the body' );
+ clean();
+}
+
+
--- /dev/null
+#!perl
+
+#****h* ROBODoc System Tests/Handling Errors
+# FUNCTION
+# Test wether calling ROBODoc with wrong options or input leads
+# to the correct error messages.
+#*****
+
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+
+# A dummy header to put into dummy source files.
+#
+my $source = <<'EOF';
+/****f* Test/test
+ * NAME
+ * Test
+ ******
+ */
+EOF
+
+#****x* Handling Errors/Error on Non-existing Option
+# FUNCTION
+# ROBODoc should complain about non-existing options.
+# SOURCE
+#
+{
+ my ($out, $err) = runrobo( '--foobar' );
+ like($out, qr/Usage/, 'Unknown option should print usage' );
+ like($err, qr/Invalid/, 'and an error message' );
+}
+#*****
+
+
+#****x* Handling Errors/Error on Mutual Excluding Options
+# FUNCTION
+# ROBODoc should complain about options that can not be used
+# together.
+# SOURCE
+#
+{
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --singledoc --html) );
+ like($out, qr/Usage/, 'Mutual excluding options should print usage' );
+ print $err;
+ like($err, qr/together/, 'and an error message' );
+ clean();
+}
+#*****
+
+#****x* Handling Errors/Error on Mutual Excluding Options II
+# FUNCTION
+# ROBODoc should complain about options that can not be used
+# together.
+# SOURCE
+#
+{
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test --dbxml --html) );
+ like($out, qr/Usage/, 'Mutual excluding options should print usage' );
+ print $err;
+ like($err, qr/together/, 'and an error message' );
+ clean();
+}
+#*****
+
+#****x* Handling Errors/Error on Duplicate Options
+# FUNCTION
+# ROBODoc should complain about options that are specifed more
+# than once.
+# SOURCE
+#
+{
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw( --src Src --doc Doc --multidoc --test --test --test) );
+ print $out;
+ like($out, qr/Usage/, 'Duplicate options should print usage' );
+ print $err;
+ like($err, qr/than\sonce/, 'and an error message' );
+ clean();
+}
+#*****
+
+#****x* Handling Errors/Error on Non-existing rc file
+# FUNCTION
+# When given a non-existing .rc file, ROBODc should
+# at least report the name of the .rc file.
+# SOURCE
+{
+ my ($out, $err) = runrobo( '--rc foobar.rc' );
+ like($err, qr/foobar/, 'should give an error message about foorbar.rc' );
+}
+#*****
+
+#****x* Handling Errors/Impossible output file
+# FUNCTION
+# When given a impossible output filename ROBODoc should
+# at least report the filename.
+# SOURCE
+{
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Foo/Bar/document --singledoc --html) );
+ like($err, qr/document/, 'should give an error message about document' );
+}
+
+#*****
+
+#****x* Handling Errors/Non-existing css file
+# FUNCTION
+# When given a impossible css filename ROBODoc should
+# at least report the filename.
+# SOURCE
+{
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --html --css Foo/Bar/cascade.css) );
+ like($err, qr/cascade/, 'should give an error message about css file' );
+}
+
+#*****
+
+
+
+#****x* Handling Errors/Header without end marker
+# FUNCTION
+# Test ROBODoc's response on a header without an
+# end marker. ROBODoc should detect this.
+# SOURCE
+#
+
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ * SOURCE
+ */
+
+some source
+
+and no end of the header ....
+
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test) );
+ is( $out, '', 'no output' );
+ like( $err, qr/end\smarker/, 'error about end marker' );
+ clean();
+}
+
+#****
+
+#****x* Handling Errors/Headers with duplicate names
+# FUNCTION
+# Test ROBODoc's response to a file with two headers that
+# have the same name. This should be reported as an error.
+# SOURCE
+
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ *****
+ */
+
+/****f* Test/test
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ *****
+ */
+
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test) );
+ is( $out, '', 'no output' );
+ like( $err, qr/already\sexists/, 'error duplicate header' );
+ clean();
+}
+
+#*****
+
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * FOO
+ * test
+ ******/
+EOF
+
+ my $rc_file = <<'EOF';
+items:
+ FOO
+ BAR
+ SNAFU
+item order:
+ FOO
+ BARRRRRR
+ SNAFU
+EOF
+
+ add_configuration( "robodoc.rc", $rc_file );
+ add_source( "test.c", $source );
+ mkdocdir();
+
+ my ($out, $err) = runrobo( qw(
+ --src Src
+ --doc Doc
+ --multidoc
+ --rc Config/robodoc.rc --test) );
+ is( $out, '', 'no output' );
+ like( $err, qr/item/, 'An unknown item is reported' );
+
+ clean();
+}
--- /dev/null
+#!perl
+# vi: spell ff=unix
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use XML::Simple;
+
+#****h* ROBODoc System Tests/Header Test
+# FUNCTION
+# Tests the parsing of ROBODoc headers.
+#
+#*****
+
+#****x* Header Test/Happy Path
+# FUNCTION
+# Happy Path Simple plain header. This definitely should work
+# SOURCE
+#
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ ******
+ */
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'and no error' );
+ clean();
+}
+#*******
+
+
+#****x* Header Test/Names with Spaces
+# FUNCTION
+# Try a header name with spaces and some '*' at
+# the end. The '*' should be ignored.
+# SOURCE
+{
+ my $source = <<'EOF';
+/****f* Test Foo Bar/Name With Spaces ****
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ ******
+ */
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'and no error' );
+ my $documentation = XMLin( 'Doc/test_c.xml' );
+ my $header = $documentation->{'header'};
+ is ( $header->{'name'}, 'Test Foo Bar/Name With Spaces', 'Header name' );
+
+ clean();
+}
+
+#*****
+
+#****x* Header Test/Multiple Names with Spaces
+# FUNCTION
+# Try several header with names that contain spaces.
+# These should be accepted.
+# SOURCE
+{
+ my $source = <<'EOF';
+/****f* Test Foo Bar/Name With Spaces, And Anotherone,
+ * And One More, More
+ * NAME
+ * Test
+ * FUNCTION
+ * Test2
+ ******
+ */
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc --test) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'and no error' );
+ my $documentation = XMLin( 'Doc/test_c.xml' );
+ my $header = $documentation->{'header'};
+ is ( $header->{'name'}, 'Test Foo Bar/Name With Spaces', 'Header name' );
+
+ clean();
+}
+
+#*****
+
+#****x* Header Test/Broken header
+# FUNCTION
+# Try several header with names that contain spaces.
+# These should be accepted.
+# SOURCE
+{
+ my $source = <<'EOF';
+{****f* xxxxxx/x_xxx_xxxxxxxxxxxxxxxxxxxxxxxx
+*
+*OMSCHRIJVING
+*xxxx xxxxxxx xxxxxxxxx xxxxxxxxxx xxxxxxx xxxxxxxxxxxx xx xx
+xxxxxxxxxxxxxxx
+*xxxxxxxxx x_xxx_xxxxxxxxxxxxxxxxxx xxxxxxx
+*
+*INVOER
+*xxxxxxx : xxx xxxxxxx xxxx xxxxxx xxxxxxxxx xxxxxxxxxxxx xxxxx xx xxx
+xx
+* xxxxxxxxx xxxxxxxxx xxxxx xxxxxxxxxxxx.
+*xxxxxxxxxxx : xxx xxxxxxxxx xxxxx xxxxx xxx xx xxxxx xxxxxx xx xxxxxxxxxx
+* xxxxxxxxx xxxxxxxx xx
+*RETURNWAARDE
+*xxxx__xxxxxxx, x_xxx_xxxxxxxxx, xxxx__xxxxxxxx, x_xxx_xxxxxxxx,
+*x_xxx_xxxxxxxxx, x_xxx_xxxxxxxxxxxxxx xx x_xxxx_xxxxxx_xxx
+xxxxxx xxxxx.
+*
+*VOORBEELD
+*x_xxx_xxxxxxxxxxxxxxxxxxxxxxxx (xxxxxxx := 5000,
+* xxxxxxxxxxx := xx_xxxxxxxxxxx);
+*DECLARATIE
+*}
+
+{*****}
+EOF
+
+
+ my $config = <<'EOF';
+items:
+ OMSCHRIJVING
+ WIJZIGINGSOVERZICHT
+ DECLARATIE
+ INVOER
+ UITVOER
+ IN-UITVOER
+ PARAMETERS
+ BOUNDARIES
+ RETURNWAARDE
+ ATTRIBUTEN
+ GEBRUIKTE GEGEVENS
+ VOORBEELD
+ ZIE VERDER
+ OPMERKINGEN
+ PRECONDITIE
+ POSTCONDITIE
+
+item order:
+ OMSCHRIJVING
+ DECLARATIE
+
+source items:
+ DECLARATIE
+ SOURCE
+
+preformatted items:
+ WIJZIGINGSOVERZICHT
+ INVOER
+ UITVOER
+ IN-UITVOER
+ PARAMETERS
+ RETURNWAARDE
+ ATTRIBUTEN
+ VOORBEELD
+
+headertypes:
+ h "Modules" vptlib_modules
+ f "Functies" vptlib_functies
+ v "Variabelen" vptlib_variabelen
+ s "Structuren" vptlib_structuren
+ e "Enumeraties" vptlib_enumeraties
+ c "Constanten" vptlib_constanten
+ u "Unittesten" vptlib_unittesten
+ t "Typedefinities" vptlib_typedefs
+ m "Macros" vptlib_macros
+ d "Definities" vptlib_definities
+
+options:
+ --documenttitle "Xxxxxxxxxxxxxxxxxxx"
+ --tabsize 8
+ --index
+ --sections
+ --sectionnameonly
+ --nopre
+ --multidoc
+
+header markers:
+ /****
+ #****
+ !****
+ {****
+
+remark markers:
+ *
+ #
+ !
+
+end markers:
+ ****
+ #****
+ !****
+ {****
+
+remark begin markers:
+ /*
+ {*
+
+remark end markers:
+ */
+ *}
+
+source line comments:
+ //
+ !
+ *
+EOF
+
+ add_source( "test.c", $source );
+ add_configuration( "test.rc", $config );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --rc Config/test.rc --test) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'and no error' );
+
+ clean();
+}
+
+#*****
+
+
+
+#****x* Header Test/Circular header
+# FUNCTION
+# The 3rd header points to the first header,
+# completing a circle. ROBODoc should complain
+# about this.
+#
+# SOURCE
+TODO: {
+ local $TODO = "checking circular headers is not implemented yet.";
+ my $source = <<'EOF';
+
+/****f* Foo/Bar
+* NAME
+* Bar
+****/
+
+/****f* Bar/Fii
+ * NAME
+ * Foo
+ *****/
+
+/****f* Fii/Bar
+ * NAME
+ * Foo
+ *****/
+EOF
+
+ add_source( "test.c", $source );
+ my ($out, $err) = runrobo( qw(
+ --src Src
+ --doc Doc
+ --multidoc
+ --html
+ --toc
+ --sections
+ ) );
+ # ROBODoc should complain about circular
+ # headers.
+ isnt( $err, '', 'there should be an error' );
+# clean();
+}
+
+#*****
--- /dev/null
+#!perl
+# vi: spell ff=unix
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+
+#****h* ROBODoc System Tests/Item Test
+# FUNCTION
+# Tests the parsing generation of ROBODoc items.
+#
+#*****
+
+#****x* Item Test/Sorting
+# FUNCTION
+# Lets see if we can get items sorted according
+# to the order specified in a robodoc.rc file.
+#
+# SOURCE
+#
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * FIRST
+ * Test 1
+ * Second
+ * Test 2
+ * THIRD
+ * Test 3
+ * SOURCE
+ */
+
+ Test 4
+
+ /*******/
+EOF
+
+ my $config = <<'EOF';
+items:
+ FIRST
+ Second
+ THIRD
+ FOURTH
+item order:
+ SOURCE
+ THIRD
+EOF
+
+ my $config_no_sort = <<'EOF';
+items:
+ FIRST
+ Second
+ THIRD
+ FOURTH
+EOF
+
+ add_source( "test.c", $source );
+ add_configuration( "robodoc.rc", $config );
+ my ($out, $err) = runrobo( qw(
+ --src Src
+ --doc Doc --multidoc --ascii
+ --rc Config/robodoc.rc
+ ) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'and no error' );
+ my $documentation = IO::File->new( "<Doc/test_c.txt" );
+ ok( $documentation, 'There is a doc file' );
+ my @items = ();
+ while ( my $line = <$documentation> ) {
+ if ( $line =~ m/(SOURCE|THIRD|Second|FIRST)/ ) {
+ push( @items, $1 );
+ }
+ }
+ is( $items[ 0 ], 'SOURCE', 'First item it the source item' );
+ is( $items[ 1 ], 'THIRD', 'Second item it the third item' );
+ is( $items[ 2 ], 'FIRST', 'Third item it the first item' );
+ $documentation->close();
+
+ # Now the same but without sorting
+ add_configuration( "robodoc.rc", $config_no_sort );
+ my ($out2, $err2) = runrobo( qw(
+ --src Src
+ --doc Doc --multidoc --ascii
+ --rc Config/robodoc.rc
+ ) );
+ is( $out2, '', 'no output' );
+ is( $err2, '', 'and no error' );
+
+ $documentation = IO::File->new( "<Doc/test_c.txt" );
+ ok( $documentation, 'There is a doc file' );
+ @items = ();
+ while ( my $line = <$documentation> ) {
+ if ( $line =~ m/(SOURCE|THIRD|Second|FIRST)/ ) {
+ push( @items, $1 );
+ }
+ }
+ is( $items[ 0 ], 'FIRST', 'First item it the first item' );
+ is( $items[ 1 ], 'Second', 'Second item it the second item' );
+ is( $items[ 2 ], 'THIRD', 'Third item it the third item' );
+ is( $items[ 3 ], 'SOURCE', 'Fourth item it the fourth' );
+ $documentation->close();
+
+ clean();
+}
+#*******
+
+
--- /dev/null
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+
+#****h* ROBODoc System Tests/LaTeX Generator
+# FUNCTION
+# Test ROBODoc LaTeX generator.
+#
+#****
+
+
+#****v* LaTeX Generator/dummy_header_1
+# FUNCTION
+# A dummy header to put into dummy source files.
+# SOURCE
+#
+ my $dummy_header_1 = <<'EOF';
+C ****f* Lib/Func
+C NAME
+C Func -- useless
+C Computes the value:
+C |latex \begin{equation}
+C |latex x = 0
+C |latex \end{equation}
+C app
+C
+C SYNOPSIS
+C Func = Func (n)
+C Computes the value:
+C |latex \begin{equation}
+C |latex x = 0
+C |latex \end{equation}
+C BUGS
+C Generates screwy TeX
+C ***
+ real function Func(n)
+ Func = 0
+ end function Func
+
+EOF
+
+#****
+
+
+#****v* LaTeX Generator/dummy_header_2
+# FUNCTION
+# A dummy header to put into dummy source files.
+# SOURCE
+#
+ my $dummy_header_2 = <<'EOF';
+C ****f* Lib/Func
+C NAME
+C Func -- useless
+C SYNOPSIS
+C Example:
+C Foo foo foo
+C foo
+C
+C Test paragraph.
+C Do da diddi do da dom dom.
+C
+C BUGS
+C A list test:
+C * item 1
+C * item 2
+C * item 3
+C
+C ***
+ real function Func(n)
+ Func = 0
+ end function Func
+
+EOF
+
+#****
+
+
+#****x* LaTeX Generator/latex is balanced
+# FUNCTION
+# This function tests whether a generated latex file is balanced
+# or not. That is every
+# /begin{xxx}
+# should end with a
+# /end{xxx}
+# at the same level.
+#
+# This is tested with several headers and in different modes.
+#
+# SOURCE
+{
+ my @sources = ( \$dummy_header_1, \$dummy_header_2 );
+
+ foreach my $source_ref ( @sources ) {
+ foreach my $mode_1 qw( --sections --toc --index ) {
+ foreach my $mode_2 qw( --nopre --altlatex ) {
+ mkdocdir();
+ add_source( "test.c", $$source_ref );
+ my ( $out, $err ) = runrobo(
+ qw(--src Src
+ --doc Doc/test
+ --singledoc
+ --latex
+ ), $mode_1, $mode_2 );
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "Doc/test.tex", 'there should be documentation' );
+ is( is_latex_balanced( "Doc/test.tex" ), 1, 'latex is balanced' );
+ clean();
+ }
+ }
+ }
+}
+#****
+
--- /dev/null
+package IPC::Run ;
+#
+# Copyright (c) 1999 by Barrie Slaymaker, barries@slaysys.com
+#
+# You may distribute under the terms of either the GNU General Public
+# License or the Artistic License, as specified in the README file.
+#
+
+$VERSION = "0.80";
+
+=head1 NAME
+
+IPC::Run - system() and background procs w/ piping, redirs, ptys (Unix, Win32)
+
+=head1 SYNOPSIS
+
+ ## First,a command to run:
+ my @cat = qw( cat ) ;
+
+ ## Using run() instead of system():
+ use IPC::Run qw( run timeout ) ;
+
+ run \@cmd, \$in, \$out, \$err, timeout( 10 ) or die "cat: $?"
+
+ # Can do I/O to sub refs and filenames, too:
+ run \@cmd, '<', "in.txt", \&out, \&err or die "cat: $?"
+ run \@cat, '<', "in.txt", '>>', "out.txt", '2>>', "err.txt" ;
+
+
+ # Redirecting using psuedo-terminals instad of pipes.
+ run \@cat, '<pty<', \$in, '>pty>', \$out_and_err ;
+
+ ## Scripting subprocesses (like Expect):
+
+ use IPC::Run qw( start pump finish timeout ) ;
+
+ # Incrementally read from / write to scalars.
+ # $in is drained as it is fed to cat's stdin,
+ # $out accumulates cat's stdout
+ # $err accumulates cat's stderr
+ # $h is for "harness".
+ my $h = start \@cat, \$in, \$out, \$err, timeout( 10 ) ;
+
+ $in .= "some input\n" ;
+ pump $h until $out =~ /input\n/g ;
+
+ $in .= "some more input\n" ;
+ pump $h until $out =~ /\G.*more input\n/ ;
+
+ $in .= "some final input\n" ;
+ finish $h or die "cat returned $?" ;
+
+ warn $err if $err ;
+ print $out ; ## All of cat's output
+
+ # Piping between children
+ run \@cat, '|', \@gzip ;
+
+ # Multiple children simultaneously (run() blocks until all
+ # children exit, use start() for background execution):
+ run \@foo1, '&', \@foo2 ;
+
+ # Calling \&set_up_child in the child before it executes the
+ # command (only works on systems with true fork() & exec())
+ # exceptions thrown in set_up_child() will be propagated back
+ # to the parent and thrown from run().
+ run \@cat, \$in, \$out,
+ init => \&set_up_child ;
+
+ # Read from / write to file handles you open and close
+ open IN, '<in.txt' or die $! ;
+ open OUT, '>out.txt' or die $! ;
+ print OUT "preamble\n" ;
+ run \@cat, \*IN, \*OUT or die "cat returned $?" ;
+ print OUT "postamble\n" ;
+ close IN ;
+ close OUT ;
+
+ # Create pipes for you to read / write (like IPC::Open2 & 3).
+ $h = start
+ \@cat,
+ '<pipe', \*IN,
+ '>pipe', \*OUT,
+ '2>pipe', \*ERR
+ or die "cat returned $?" ;
+ print IN "some input\n" ;
+ close IN ;
+ print <OUT>, <ERR> ;
+ finish $h ;
+
+ # Mixing input and output modes
+ run \@cat, 'in.txt', \&catch_some_out, \*ERR_LOG ) ;
+
+ # Other redirection constructs
+ run \@cat, '>&', \$out_and_err ;
+ run \@cat, '2>&1' ;
+ run \@cat, '0<&3' ;
+ run \@cat, '<&-' ;
+ run \@cat, '3<', \$in3 ;
+ run \@cat, '4>', \$out4 ;
+ # etc.
+
+ # Passing options:
+ run \@cat, 'in.txt', debug => 1 ;
+
+ # Call this system's shell, returns TRUE on 0 exit code
+ # THIS IS THE OPPOSITE SENSE OF system()'s RETURN VALUE
+ run "cat a b c" or die "cat returned $?" ;
+
+ # Launch a sub process directly, no shell. Can't do redirection
+ # with this form, it's here to behave like system() with an
+ # inverted result.
+ $r = run "cat a b c" ;
+
+ # Read from a file in to a scalar
+ run io( "filename", 'r', \$recv ) ;
+ run io( \*HANDLE, 'r', \$recv ) ;
+
+=head1 DESCRIPTION
+
+IPC::Run allows you run and interact with child processes using files, pipes,
+and pseudo-ttys. Both system()-style and scripted usages are supported and
+may be mixed. Likewise, functional and OO API styles are both supported and
+may be mixed.
+
+Various redirection operators reminiscent of those seen on common Unix and DOS
+command lines are provided.
+
+Before digging in to the details a few LIMITATIONS are important enough
+to be mentioned right up front:
+
+=over
+
+=item Win32 Support
+
+Win32 support is working but B<EXPERIMENTAL>, but does pass all relevant tests
+on NT 4.0. See L</Win32 LIMITATIONS>.
+
+=item pty Support
+
+If you need pty support, IPC::Run should work well enough most of the
+time, but IO::Pty is being improved, and IPC::Run will be improved to
+use IO::Pty's new features when it is release.
+
+The basic problem is that the pty needs to initialize itself before the
+parent writes to the master pty, or the data written gets lost. So
+IPC::Run does a sleep(1) in the parent after forking to (hopefully) give
+the child a chance to run. This is a kludge that works well on non
+heavily loaded systems :(.
+
+ptys are not supported yet under Win32, but will be emulated...
+
+=item Debugging Tip
+
+You may use the environment variable C<IPCRUNDEBUG> to see what's going on
+under the hood:
+
+ $ IPCRUNDEBUG=basic myscript # prints minimal debugging
+ $ IPCRUNDEBUG=data myscript # prints all data reads/writes
+ $ IPCRUNDEBUG=details myscript # prints lots of low-level details
+ $ IPCRUNDEBUG=gory myscript # (Win32 only) prints data moving through
+ # the helper processes.
+
+=back
+
+We now return you to your regularly scheduled documentation.
+
+=head2 Harnesses
+
+Child processes and I/O handles are gathered in to a harness, then
+started and run until the processing is finished or aborted.
+
+=head2 run() vs. start(); pump(); finish();
+
+There are two modes you can run harnesses in: run() functions as an
+enhanced system(), and start()/pump()/finish() allow for background
+processes and scripted interactions with them.
+
+When using run(), all data to be sent to the harness is set up in
+advance (though one can feed subprocesses input from subroutine refs to
+get around this limitation). The harness is run and all output is
+collected from it, then any child processes are waited for:
+
+ run \@cmd, \<<IN, \$out ;
+ blah
+ IN
+
+ ## To precompile harnesses and run them later:
+ my $h = harness \@cmd, \<<IN, \$out ;
+ blah
+ IN
+
+ run $h ;
+
+The background and scripting API is provided by start(), pump(), and
+finish(): start() creates a harness if need be (by calling harness())
+and launches any subprocesses, pump() allows you to poll them for
+activity, and finish() then monitors the harnessed activities until they
+complete.
+
+ ## Build the harness, open all pipes, and launch the subprocesses
+ my $h = start \@cat, \$in, \$out ;
+ $in = "first input\n" ;
+
+ ## Now do I/O. start() does no I/O.
+ pump $h while length $in ; ## Wait for all input to go
+
+ ## Now do some more I/O.
+ $in = "second input\n" ;
+ pump $h until $out =~ /second input/ ;
+
+ ## Clean up
+ finish $h or die "cat returned $?" ;
+
+You can optionally compile the harness with harness() prior to
+start()ing or run()ing, and you may omit start() between harness() and
+pump(). You might want to do these things if you compile your harnesses
+ahead of time.
+
+=head2 Using regexps to match output
+
+As shown in most of the scripting examples, the read-to-scalar facility
+for gathering subcommand's output is often used with regular expressions
+to detect stopping points. This is because subcommand output often
+arrives in dribbles and drabs, often only a character or line at a time.
+This output is input for the main program and piles up in variables like
+the C<$out> and C<$err> in our examples.
+
+Regular expressions can be used to wait for appropriate output in
+several ways. The C<cat> example in the previous section demonstrates
+how to pump() until some string appears in the output. Here's an
+example that uses C<smb> to fetch files from a remote server:
+
+ $h = harness \@smbclient, \$in, \$out ;
+
+ $in = "cd /src\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/m ;
+ die "error cding to /src:\n$out" if $out =~ "ERR" ;
+ $out = '' ;
+
+ $in = "mget *\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/m ;
+ die "error retrieving files:\n$out" if $out =~ "ERR" ;
+
+ $in = "quit\n" ;
+ $h->finish ;
+
+Notice that we carefully clear $out after the first command/response
+cycle? That's because IPC::Run does not delete $out when we continue,
+and we don't want to trip over the old output in the second
+command/response cycle.
+
+Say you want to accumulate all the output in $out and analyze it
+afterwards. Perl offers incremental regular expression matching using
+the C<m//gc> and pattern matching idiom and the C<\G> assertion.
+IPC::Run is careful not to disturb the current C<pos()> value for
+scalars it appends data to, so we could modify the above so as not to
+destroy $out by adding a couple of C</gc> modifiers. The C</g> keeps us
+from tripping over the previous prompt and the C</c> keeps us from
+resetting the prior match position if the expected prompt doesn't
+materialize immediately:
+
+ $h = harness \@smbclient, \$in, \$out ;
+
+ $in = "cd /src\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/mgc ;
+ die "error cding to /src:\n$out" if $out =~ "ERR" ;
+
+ $in = "mget *\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/mgc ;
+ die "error retrieving files:\n$out" if $out =~ "ERR" ;
+
+ $in = "quit\n" ;
+ $h->finish ;
+
+ analyze( $out ) ;
+
+When using this technique, you may want to preallocate $out to have
+plenty of memory or you may find that the act of growing $out each time
+new input arrives causes an C<O(length($out)^2)> slowdown as $out grows.
+Say we expect no more than 10,000 characters of input at the most. To
+preallocate memory to $out, do something like:
+
+ my $out = "x" x 10_000 ;
+ $out = "" ;
+
+C<perl> will allocate at least 10,000 characters' worth of space, then
+mark the $out as having 0 length without freeing all that yummy RAM.
+
+=head2 Timeouts and Timers
+
+More than likely, you don't want your subprocesses to run forever, and
+sometimes it's nice to know that they're going a little slowly.
+Timeouts throw exceptions after a some time has elapsed, timers merely
+cause pump() to return after some time has elapsed. Neither is
+reset/restarted automatically.
+
+Timeout objects are created by calling timeout( $interval ) and passing
+the result to run(), start() or harness(). The timeout period starts
+ticking just after all the child processes have been fork()ed or
+spawn()ed, and are polled for expiration in run(), pump() and finish().
+If/when they expire, an exception is thrown. This is typically useful
+to keep a subprocess from taking too long.
+
+If a timeout occurs in run(), all child processes will be terminated and
+all file/pipe/ptty descriptors opened by run() will be closed. File
+descriptors opened by the parent process and passed in to run() are not
+closed in this event.
+
+If a timeout occurs in pump(), pump_nb(), or finish(), it's up to you to
+decide whether to kill_kill() all the children or to implement some more
+graceful fallback. No I/O will be closed in pump(), pump_nb() or
+finish() by such an exception (though I/O is often closed down in those
+routines during the natural course of events).
+
+Often an exception is too harsh. timer( $interval ) creates timer
+objects that merely prevent pump() from blocking forever. This can be
+useful for detecting stalled I/O or printing a soothing message or "."
+to pacify an anxious user.
+
+Timeouts and timers can both be restarted at any time using the timer's
+start() method (this is not the start() that launches subprocesses). To
+restart a timer, you need to keep a reference to the timer:
+
+ ## Start with a nice long timeout to let smbclient connect. If
+ ## pump or finish take too long, an exception will be thrown.
+
+ my $h ;
+ eval {
+ $h = harness \@smbclient, \$in, \$out, \$err, ( my $t = timeout 30 ) ;
+ sleep 11 ; # No effect: timer not running yet
+
+ start $h ;
+ $in = "cd /src\n" ;
+ pump $h until ! length $in ;
+
+ $in = "ls\n" ;
+ ## Now use a short timeout, since this should be faster
+ $t->start( 5 ) ;
+ pump $h until ! length $in ;
+
+ $t->start( 10 ) ; ## Give smbclient a little while to shut down.
+ $h->finish ;
+ } ;
+ if ( $@ ) {
+ my $x = $@ ; ## Preserve $@ in case another exception occurs
+ $h->kill_kill ; ## kill it gently, then brutally if need be, or just
+ ## brutally on Win32.
+ die $x ;
+ }
+
+Timeouts and timers are I<not> checked once the subprocesses are shut
+down; they will not expire in the interval between the last valid
+process and when IPC::Run scoops up the processes' result codes, for
+instance.
+
+=head2 Spawning synchronization, child exception propagation
+
+start() pauses the parent until the child executes the command or CODE
+reference and propagates any exceptions thrown (including exec()
+failure) back to the parent. This has several pleasant effects: any
+exceptions thrown in the child, including exec() failure, come flying
+out of start() or run() as though they had ocurred in the parent.
+
+This includes exceptions your code thrown from init subs. In this
+example:
+
+ eval {
+ run \@cmd, init => sub { die "blast it! foiled again!" } ;
+ } ;
+ print $@ ;
+
+the exception "blast it! foiled again" will be thrown from the child
+process (preventing the exec()) and printed by the parent.
+
+In situations like
+
+ run \@cmd1, "|", \@cmd2, "|", \@cmd3 ;
+
+@cmd1 will be initted and exec()ed before @cmd2, and @cmd2 before @cmd3.
+This can save time and prevent oddball errors emitted by later commands
+when earlier commands fail to execute. Note that IPC::Run doesn't start
+any commands unless it can find the executables referenced by all
+commands. These executables must pass both the C<-f> and C<-x> tests
+described in L<perlfunc>.
+
+Another nice effect is that init() subs can take their time doing things
+and there will be no problems caused by a parent continuing to execute
+before a child's init() routine is complete. Say the init() routine
+needs to open a socket or a temp file that the parent wants to connect
+to; without this synchronization, the parent will need to implement a
+retry loop to wait for the child to run, since often, the parent gets a
+lot of things done before the child's first timeslice is allocated.
+
+This is also quite necessary for pseudo-tty initialization, which needs
+to take place before the parent writes to the child via pty. Writes
+that occur before the pty is set up can get lost.
+
+A final, minor, nicety is that debugging output from the child will be
+emitted before the parent continues on, making for much clearer debugging
+output in complex situations.
+
+The only drawback I can conceive of is that the parent can't continue to
+operate while the child is being initted. If this ever becomes a
+problem in the field, we can implement an option to avoid this behavior,
+but I don't expect it to.
+
+B<Win32>: executing CODE references isn't supported on Win32, see
+L</Win32 LIMITATIONS> for details.
+
+=head2 Syntax
+
+run(), start(), and harness() can all take a harness specification
+as input. A harness specification is either a single string to be passed
+to the systems' shell:
+
+ run "echo 'hi there'" ;
+
+or a list of commands, io operations, and/or timers/timeouts to execute.
+Consecutive commands must be separated by a pipe operator '|' or an '&'.
+External commands are passed in as array references, and, on systems
+supporting fork(), Perl code may be passed in as subs:
+
+ run \@cmd ;
+ run \@cmd1, '|', \@cmd2 ;
+ run \@cmd1, '&', \@cmd2 ;
+ run \&sub1 ;
+ run \&sub1, '|', \&sub2 ;
+ run \&sub1, '&', \&sub2 ;
+
+'|' pipes the stdout of \@cmd1 the stdin of \@cmd2, just like a
+shell pipe. '&' does not. Child processes to the right of a '&'
+will have their stdin closed unless it's redirected-to.
+
+L<IPC::Run::IO> objects may be passed in as well, whether or not
+child processes are also specified:
+
+ run io( "infile", ">", \$in ), io( "outfile", "<", \$in ) ;
+
+as can L<IPC::Run::Timer> objects:
+
+ run \@cmd, io( "outfile", "<", \$in ), timeout( 10 ) ;
+
+Commands may be followed by scalar, sub, or i/o handle references for
+redirecting
+child process input & output:
+
+ run \@cmd, \undef, \$out ;
+ run \@cmd, \$in, \$out ;
+ run \@cmd1, \&in, '|', \@cmd2, \*OUT ;
+ run \@cmd1, \*IN, '|', \@cmd2, \&out ;
+
+This is known as succinct redirection syntax, since run(), start()
+and harness(), figure out which file descriptor to redirect and how.
+File descriptor 0 is presumed to be an input for
+the child process, all others are outputs. The assumed file
+descriptor always starts at 0, unless the command is being piped to,
+in which case it starts at 1.
+
+To be explicit about your redirects, or if you need to do more complex
+things, there's also a redirection operator syntax:
+
+ run \@cmd, '<', \undef, '>', \$out ;
+ run \@cmd, '<', \undef, '>&', \$out_and_err ;
+ run(
+ \@cmd1,
+ '<', \$in,
+ '|', \@cmd2,
+ \$out
+ ) ;
+
+Operator syntax is required if you need to do something other than simple
+redirection to/from scalars or subs, like duping or closing file descriptors
+or redirecting to/from a named file. The operators are covered in detail
+below.
+
+After each \@cmd (or \&foo), parsing begins in succinct mode and toggles to
+operator syntax mode when an operator (ie plain scalar, not a ref) is seen.
+Once in
+operator syntax mode, parsing only reverts to succinct mode when a '|' or
+'&' is seen.
+
+In succinct mode, each parameter after the \@cmd specifies what to
+do with the next highest file descriptor. These File descriptor start
+with 0 (stdin) unless stdin is being piped to (C<'|', \@cmd>), in which
+case they start with 1 (stdout). Currently, being on the left of
+a pipe (C<\@cmd, \$out, \$err, '|'>) does I<not> cause stdout to be
+skipped, though this may change since it's not as DWIMerly as it
+could be. Only stdin is assumed to be an
+input in succinct mode, all others are assumed to be outputs.
+
+If no piping or redirection is specified for a child, it will inherit
+the parent's open file handles as dictated by your system's
+close-on-exec behavior and the $^F flag, except that processes after a
+'&' will not inherit the parent's stdin. Also note that $^F does not
+affect file desciptors obtained via POSIX, since it only applies to
+full-fledged Perl file handles. Such processes will have their stdin
+closed unless it has been redirected-to.
+
+If you want to close a child processes stdin, you may do any of:
+
+ run \@cmd, \undef ;
+ run \@cmd, \"" ;
+ run \@cmd, '<&-' ;
+ run \@cmd, '0<&-' ;
+
+Redirection is done by placing redirection specifications immediately
+after a command or child subroutine:
+
+ run \@cmd1, \$in, '|', \@cmd2, \$out ;
+ run \@cmd1, '<', \$in, '|', \@cmd2, '>', \$out ;
+
+If you omit the redirection operators, descriptors are counted
+starting at 0. Descriptor 0 is assumed to be input, all others
+are outputs. A leading '|' consumes descriptor 0, so this
+works as expected.
+
+ run \@cmd1, \$in, '|', \@cmd2, \$out ;
+
+The parameter following a redirection operator can be a scalar ref,
+a subroutine ref, a file name, an open filehandle, or a closed
+filehandle.
+
+If it's a scalar ref, the child reads input from or sends output to
+that variable:
+
+ $in = "Hello World.\n" ;
+ run \@cat, \$in, \$out ;
+ print $out ;
+
+Scalars used in incremental (start()/pump()/finish()) applications are treated
+as queues: input is removed from input scalers, resulting in them dwindling
+to '', and output is appended to output scalars. This is not true of
+harnesses run() in batch mode.
+
+It's usually wise to append new input to be sent to the child to the input
+queue, and you'll often want to zap output queues to '' before pumping.
+
+ $h = start \@cat, \$in ;
+ $in = "line 1\n" ;
+ pump $h ;
+ $in .= "line 2\n" ;
+ pump $h ;
+ $in .= "line 3\n" ;
+ finish $h ;
+
+The final call to finish() must be there: it allows the child process(es)
+to run to completion and waits for their exit values.
+
+=head1 OBSTINATE CHILDREN
+
+Interactive applications are usually optimized for human use. This
+can help or hinder trying to interact with them through modules like
+IPC::Run. Frequently, programs alter their behavior when they detect
+that stdin, stdout, or stderr are not connected to a tty, assuming that
+they are being run in batch mode. Whether this helps or hurts depends
+on which optimizations change. And there's often no way of telling
+what a program does in these areas other than trial and error and,
+occasionally, reading the source. This includes different versions
+and implementations of the same program.
+
+All hope is not lost, however. Most programs behave in reasonably
+tractable manners, once you figure out what it's trying to do.
+
+Here are some of the issues you might need to be aware of.
+
+=over
+
+=item *
+
+fflush()ing stdout and stderr
+
+This lets the user see stdout and stderr immediately. Many programs
+undo this optimization if stdout is not a tty, making them harder to
+manage by things like IPC::Run.
+
+Many programs decline to fflush stdout or stderr if they do not
+detect a tty there. Some ftp commands do this, for instance.
+
+If this happens to you, look for a way to force interactive behavior,
+like a command line switch or command. If you can't, you will
+need to use a pseudo terminal ('<pty<' and '>pty>').
+
+=item *
+
+false prompts
+
+Interactive programs generally do not guarantee that output from user
+commands won't contain a prompt string. For example, your shell prompt
+might be a '$', and a file named '$' might be the only file in a directory
+listing.
+
+This can make it hard to guarantee that your output parser won't be fooled
+into early termination of results.
+
+To help work around this, you can see if the program can alter it's
+prompt, and use something you feel is never going to occur in actual
+practice.
+
+You should also look for your prompt to be the only thing on a line:
+
+ pump $h until $out =~ /^<SILLYPROMPT>\s?\z/m ;
+
+(use C<(?!\n)\Z> in place of C<\z> on older perls).
+
+You can also take the approach that IPC::ChildSafe takes and emit a
+command with known output after each 'real' command you issue, then
+look for this known output. See new_appender() and new_chunker() for
+filters that can help with this task.
+
+If it's not convenient or possibly to alter a prompt or use a known
+command/response pair, you might need to autodetect the prompt in case
+the local version of the child program is different then the one
+you tested with, or if the user has control over the look & feel of
+the prompt.
+
+=item *
+
+Refusing to accept input unless stdin is a tty.
+
+Some programs, for security reasons, will only accept certain types
+of input from a tty. su, notable, will not prompt for a password unless
+it's connected to a tty.
+
+If this is your situation, use a pseudo terminal ('<pty<' and '>pty>').
+
+=item *
+
+Not prompting unless connected to a tty.
+
+Some programs don't prompt unless stdin or stdout is a tty. See if you can
+turn prompting back on. If not, see if you can come up with a command that
+you can issue after every real command and look for it's output, as
+IPC::ChildSafe does. There are two filters included with IPC::Run that
+can help with doing this: appender and chunker (see new_appender() and
+new_chunker()).
+
+=item *
+
+Different output format when not connected to a tty.
+
+Some commands alter their formats to ease machine parsability when they
+aren't connected to a pipe. This is actually good, but can be surprising.
+
+=back
+
+=head1 PSEUDO TERMINALS
+
+On systems providing pseudo terminals under /dev, IPC::Run can use IO::Pty
+(available on CPAN) to provide a terminal environment to subprocesses.
+This is necessary when the subprocess really wants to think it's connected
+to a real terminal.
+
+=head2 CAVEATS
+
+Psuedo-terminals are not pipes, though they are similar. Here are some
+differences to watch out for.
+
+=over
+
+=item Echoing
+
+Sending to stdin will cause an echo on stdout, which occurs before each
+line is passed to the child program. There is currently no way to
+disable this, although the child process can and should disable it for
+things like passwords.
+
+=item Shutdown
+
+IPC::Run cannot close a pty until all output has been collected. This
+means that it is not possible to send an EOF to stdin by half-closing
+the pty, as we can when using a pipe to stdin.
+
+This means that you need to send the child process an exit command or
+signal, or run() / finish() will time out. Be careful not to expect a
+prompt after sending the exit command.
+
+=item Command line editing
+
+Some subprocesses, notable shells that depend on the user's prompt
+settings, will reissue the prompt plus the command line input so far
+once for each character.
+
+=item '>pty>' means '&>pty>', not '1>pty>'
+
+The pseudo terminal redirects both stdout and stderr unless you specify
+a file descriptor. If you want to grab stderr separately, do this:
+
+ start \@cmd, '<pty<', \$in, '>pty>', \$out, '2>', \$err ;
+
+=item stdin, stdout, and stderr not inherited
+
+Child processes harnessed to a pseudo terminal have their stdin, stdout,
+and stderr completely closed before any redirection operators take
+effect. This casts of the bonds of the controlling terminal. This is
+not done when using pipes.
+
+Right now, this affects all children in a harness that has a pty in use,
+even if that pty would not affect a particular child. That's a bug and
+will be fixed. Until it is, it's best not to mix-and-match children.
+
+=back
+
+=head2 Redirection Operators
+
+ Operator SHNP Description
+ ======== ==== ===========
+ <, N< SHN Redirects input to a child's fd N (0 assumed)
+
+ >, N> SHN Redirects output from a child's fd N (1 assumed)
+ >>, N>> SHN Like '>', but appends to scalars or named files
+ >&, &> SHN Redirects stdout & stderr from a child process
+
+ <pty, N<pty S Like '<', but uses a pseudo-tty instead of a pipe
+ >pty, N>pty S Like '>', but uses a pseudo-tty instead of a pipe
+
+ N<&M Dups input fd N to input fd M
+ M>&N Dups output fd N to input fd M
+ N<&- Closes fd N
+
+ <pipe, N<pipe P Pipe opens H for caller to read, write, close.
+ >pipe, N>pipe P Pipe opens H for caller to read, write, close.
+
+'N' and 'M' are placeholders for integer file descriptor numbers. The
+terms 'input' and 'output' are from the child process's perspective.
+
+The SHNP field indicates what parameters an operator can take:
+
+ S: \$scalar or \&function references. Filters may be used with
+ these operators (and only these).
+ H: \*HANDLE or IO::Handle for caller to open, and close
+ N: "file name".
+ P: \*HANDLE opened by IPC::Run as the parent end of a pipe, but read
+ and written to and closed by the caller (like IPC::Open3).
+
+=over
+
+=item Redirecting input: [n]<, [n]<pipe
+
+You can input the child reads on file descriptor number n to come from a
+scalar variable, subroutine, file handle, or a named file. If stdin
+is not redirected, the parent's stdin is inherited.
+
+ run \@cat, \undef ## Closes child's stdin immediately
+ or die "cat returned $?" ;
+
+ run \@cat, \$in ;
+
+ run \@cat, \<<TOHERE ;
+ blah
+ TOHERE
+
+ run \@cat, \&input ; ## Calls &input, feeding data returned
+ ## to child's. Closes child's stdin
+ ## when undef is returned.
+
+Redirecting from named files requires you to use the input
+redirection operator:
+
+ run \@cat, '<.profile' ;
+ run \@cat, '<', '.profile' ;
+
+ open IN, "<foo" ;
+ run \@cat, \*IN ;
+ run \@cat, *IN{IO} ;
+
+The form used second example here is the safest,
+since filenames like "0" and "&more\n" won't confuse &run:
+
+You can't do either of
+
+ run \@a, *IN ; ## INVALID
+ run \@a, '<', *IN ; ## BUGGY: Reads file named like "*main::A"
+
+because perl passes a scalar containing a string that
+looks like "*main::A" to &run, and &run can't tell the difference
+between that and a redirection operator or a file name. &run guarantees
+that any scalar you pass after a redirection operator is a file name.
+
+If your child process will take input from file descriptors other
+than 0 (stdin), you can use a redirection operator with any of the
+valid input forms (scalar ref, sub ref, etc.):
+
+ run \@cat, '3<', \$in3 ;
+
+When redirecting input from a scalar ref, the scalar ref is
+used as a queue. This allows you to use &harness and pump() to
+feed incremental bits of input to a coprocess. See L</Coprocesses>
+below for more information.
+
+The <pipe operator opens the write half of a pipe on the filehandle
+glob reference it takes as an argument:
+
+ $h = start \@cat, '<pipe', \*IN ;
+ print IN "hello world\n" ;
+ pump $h ;
+ close IN ;
+ finish $h ;
+
+Unlike the other '<' operators, IPC::Run does nothing further with
+it: you are responsible for it. The previous example is functionally
+equivalent to:
+
+ pipe( \*R, \*IN ) or die $! ;
+ $h = start \@cat, '<', \*IN ;
+ print IN "hello world\n" ;
+ pump $h ;
+ close IN ;
+ finish $h ;
+
+This is like the behavior of IPC::Open2 and IPC::Open3.
+
+B<Win32>: The handle returned is actually a socket handle, so you can
+use select() on it.
+
+=item Redirecting output: [n]>, [n]>>, [n]>&[m], [n]>pipe
+
+You can redirect any output the child emits
+to a scalar variable, subroutine, file handle, or file name. You
+can have &run truncate or append to named files or scalars. If
+you are redirecting stdin as well, or if the command is on the
+receiving end of a pipeline ('|'), you can omit the redirection
+operator:
+
+ @ls = ( 'ls' ) ;
+ run \@ls, \undef, \$out
+ or die "ls returned $?" ;
+
+ run \@ls, \undef, \&out ; ## Calls &out each time some output
+ ## is received from the child's
+ ## when undef is returned.
+
+ run \@ls, \undef, '2>ls.err' ;
+ run \@ls, '2>', 'ls.err' ;
+
+The two parameter form guarantees that the filename
+will not be interpreted as a redirection operator:
+
+ run \@ls, '>', "&more" ;
+ run \@ls, '2>', ">foo\n" ;
+
+You can pass file handles you've opened for writing:
+
+ open( *OUT, ">out.txt" ) ;
+ open( *ERR, ">err.txt" ) ;
+ run \@cat, \*OUT, \*ERR ;
+
+Passing a scalar reference and a code reference requires a little
+more work, but allows you to capture all of the output in a scalar
+or each piece of output by a callback:
+
+These two do the same things:
+
+ run( [ 'ls' ], '2>', sub { $err_out .= $_[0] } ) ;
+
+does the same basic thing as:
+
+ run( [ 'ls' ], '2>', \$err_out ) ;
+
+The subroutine will be called each time some data is read from the child.
+
+The >pipe operator is different in concept than the other '>' operators,
+although it's syntax is similar:
+
+ $h = start \@cat, $in, '>pipe', \*OUT, '2>pipe', \*ERR ;
+ $in = "hello world\n" ;
+ finish $h ;
+ print <OUT> ;
+ print <ERR> ;
+ close OUT ;
+ close ERR ;
+
+causes two pipe to be created, with one end attached to cat's stdout
+and stderr, respectively, and the other left open on OUT and ERR, so
+that the script can manually
+read(), select(), etc. on them. This is like
+the behavior of IPC::Open2 and IPC::Open3.
+
+B<Win32>: The handle returned is actually a socket handle, so you can
+use select() on it.
+
+=item Duplicating output descriptors: >&m, n>&m
+
+This duplicates output descriptor number n (default is 1 if n is omitted)
+from descriptor number m.
+
+=item Duplicating input descriptors: <&m, n<&m
+
+This duplicates input descriptor number n (default is 0 if n is omitted)
+from descriptor number m
+
+=item Closing descriptors: <&-, 3<&-
+
+This closes descriptor number n (default is 0 if n is omitted). The
+following commands are equivalent:
+
+ run \@cmd, \undef ;
+ run \@cmd, '<&-' ;
+ run \@cmd, '<in.txt', '<&-' ;
+
+Doing
+
+ run \@cmd, \$in, '<&-' ; ## SIGPIPE recipe.
+
+is dangerous: the parent will get a SIGPIPE if $in is not empty.
+
+=item Redirecting both stdout and stderr: &>, >&, &>pipe, >pipe&
+
+The following pairs of commands are equivalent:
+
+ run \@cmd, '>&', \$out ; run \@cmd, '>', \$out, '2>&1' ;
+ run \@cmd, '>&', 'out.txt' ; run \@cmd, '>', 'out.txt', '2>&1' ;
+
+etc.
+
+File descriptor numbers are not permitted to the left or the right of
+these operators, and the '&' may occur on either end of the operator.
+
+The '&>pipe' and '>pipe&' variants behave like the '>pipe' operator, except
+that both stdout and stderr write to the created pipe.
+
+=item Redirection Filters
+
+Both input redirections and output redirections that use scalars or
+subs as endpoints may have an arbitrary number of filter subs placed
+between them and the child process. This is useful if you want to
+receive output in chunks, or if you want to massage each chunk of
+data sent to the child. To use this feature, you must use operator
+syntax:
+
+ run(
+ \@cmd
+ '<', \&in_filter_2, \&in_filter_1, $in,
+ '>', \&out_filter_1, \&in_filter_2, $out,
+ ) ;
+
+This capability is not provided for IO handles or named files.
+
+Two filters are provided by IPC::Run: appender and chunker. Because
+these may take an argument, you need to use the constructor functions
+new_appender() and new_chunker() rather than using \& syntax:
+
+ run(
+ \@cmd
+ '<', new_appender( "\n" ), $in,
+ '>', new_chunker, $out,
+ ) ;
+
+=back
+
+=head2 Just doing I/O
+
+If you just want to do I/O to a handle or file you open yourself, you
+may specify a filehandle or filename instead of a command in the harness
+specification:
+
+ run io( "filename", '>', \$recv ) ;
+
+ $h = start io( $io, '>', \$recv ) ;
+
+ $h = harness \@cmd, '&', io( "file", '<', \$send ) ;
+
+=head2 Options
+
+Options are passed in as name/value pairs:
+
+ run \@cat, \$in, debug => 1 ;
+
+If you pass the debug option, you may want to pass it in first, so you
+can see what parsing is going on:
+
+ run debug => 1, \@cat, \$in ;
+
+=over
+
+=item debug
+
+Enables debugging output in parent and child. Debugging info is emitted
+to the STDERR that was present when IPC::Run was first C<use()>ed (it's
+C<dup()>ed out of the way so that it can be redirected in children without
+having debugging output emitted on it).
+
+=back
+
+=head1 RETURN VALUES
+
+harness() and start() return a reference to an IPC::Run harness. This is
+blessed in to the IPC::Run package, so you may make later calls to
+functions as members if you like:
+
+ $h = harness( ... ) ;
+ $h->start ;
+ $h->pump ;
+ $h->finish ;
+
+ $h = start( .... ) ;
+ $h->pump ;
+ ...
+
+Of course, using method call syntax lets you deal with any IPC::Run
+subclasses that might crop up, but don't hold your breath waiting for
+any.
+
+run() and finish() return TRUE when all subcommands exit with a 0 result
+code. B<This is the opposite of perl's system() command>.
+
+All routines raise exceptions (via die()) when error conditions are
+recognized. A non-zero command result is not treated as an error
+condition, since some commands are tests whose results are reported
+in their exit codes.
+
+=head1 ROUTINES
+
+=over
+
+=cut
+
+@ISA = qw( Exporter ) ;
+
+## We use @EXPORT for the end user's convenience: there's only one function
+## exported, it's homonymous with the module, it's an unusual name, and
+## it can be suppressed by "use IPC::Run () ;".
+
+my @FILTER_IMP = qw( input_avail get_more_input ) ;
+my @FILTERS = qw(
+ new_appender
+ new_chunker
+ new_string_source
+ new_string_sink
+) ;
+my @API = qw(
+ run
+ harness start pump pumpable finish
+ signal kill_kill reap_nb
+ io timer timeout
+ close_terminal
+ binary
+) ;
+
+@EXPORT_OK = ( @API, @FILTER_IMP, @FILTERS, qw( filter_tests Win32_MODE ) ) ;
+%EXPORT_TAGS = (
+ 'filter_imp' => \@FILTER_IMP,
+ 'all' => \@EXPORT_OK,
+ 'filters' => \@FILTERS,
+ 'api' => \@API,
+) ;
+
+use strict ;
+
+use IPC::Run::Debug;
+use Exporter ;
+use Fcntl ;
+use POSIX () ;
+use Symbol ;
+use Carp ;
+use File::Spec ;
+use IO::Handle ;
+require IPC::Run::IO ;
+require IPC::Run::Timer ;
+use UNIVERSAL qw( isa ) ;
+
+use constant Win32_MODE => $^O =~ /os2|Win32/i ;
+
+BEGIN {
+ if ( Win32_MODE ) {
+ eval "use IPC::Run::Win32Helper; 1;"
+ or ( $@ && die ) or die "$!" ;
+ }
+ else {
+ eval "use File::Basename; 1;" or die $! ;
+ }
+}
+
+
+sub input_avail() ;
+sub get_more_input() ;
+
+###############################################################################
+
+##
+## State machine states, set in $self->{STATE}
+##
+## These must be in ascending order numerically
+##
+sub _newed() {0}
+sub _harnessed(){1}
+sub _finished() {2} ## _finished behave almost exactly like _harnessed
+sub _started() {3}
+
+##
+## Which fds have been opened in the parent. This may have extra fds, since
+## we aren't all that rigorous about closing these off, but that's ok. This
+## is used on Unixish OSs to close all fds in the child that aren't needed
+## by that particular child.
+my %fds ;
+
+## There's a bit of hackery going on here.
+##
+## We want to have any code anywhere be able to emit
+## debugging statements without knowing what harness the code is
+## being called in/from, since we'd need to pass a harness around to
+## everything.
+##
+## Thus, $cur_self was born.
+
+use vars qw( $cur_self ) ;
+
+sub _debug_fd {
+ return fileno STDERR unless defined $cur_self ;
+
+ if ( _debugging && ! defined $cur_self->{DEBUG_FD} ) {
+ my $fd = select STDERR ; $| = 1 ; select $fd ;
+ $cur_self->{DEBUG_FD} = POSIX::dup fileno STDERR ;
+ _debug( "debugging fd is $cur_self->{DEBUG_FD}\n" )
+ if _debugging_details ;
+ }
+
+ return fileno STDERR unless defined $cur_self->{DEBUG_FD} ;
+
+ return $cur_self->{DEBUG_FD}
+}
+
+sub DESTROY {
+ ## We absolutely do not want to do anything else here. We are likely
+ ## to be in a child process and we don't want to do things like kill_kill
+ ## ourself or cause other destruction.
+ my IPC::Run $self = shift ;
+ POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
+ $self->{DEBUG_FD} = undef ;
+}
+
+##
+## Support routines (NOT METHODS)
+##
+my %cmd_cache ;
+
+sub _search_path {
+ my ( $cmd_name ) = @_ ;
+ if ( File::Spec->file_name_is_absolute( $cmd_name ) && -x $cmd_name) {
+ _debug "'", $cmd_name, "' is absolute"
+ if _debugging_details ;
+ return $cmd_name ;
+ }
+
+ my $dirsep =
+ ( Win32_MODE
+ ? '[/\\\\]'
+ : $^O =~ /MacOS/
+ ? ':'
+ : $^O =~ /VMS/
+ ? '[\[\]]'
+ : '/'
+ ) ;
+
+ if ( Win32_MODE
+ && ( $cmd_name =~ /$dirsep/ )
+ && ( $cmd_name !~ /\..+$/ ) ## Only run if cmd_name has no extension?
+ ) {
+ for ( split /;/, $ENV{PATHEXT} || ".COM;.BAT;.EXE" ) {
+ my $name = "$cmd_name$_";
+ $cmd_name = $name, last if -f $name && -x _;
+ }
+ }
+
+ if ( $cmd_name =~ /($dirsep)/ ) {
+ _debug "'$cmd_name' contains '$1'" if _debugging;
+ croak "file not found: $cmd_name" unless -e $cmd_name ;
+ croak "not a file: $cmd_name" unless -f $cmd_name ;
+ croak "permission denied: $cmd_name" unless -x $cmd_name ;
+ return $cmd_name ;
+ }
+
+ if ( exists $cmd_cache{$cmd_name} ) {
+ _debug "'$cmd_name' found in cache: '$cmd_cache{$cmd_name}'"
+ if _debugging;
+ return $cmd_cache{$cmd_name} if -x $cmd_cache{$cmd_name} ;
+ _debug "'$cmd_cache{$cmd_name}' no longer executable, searching..."
+ if _debugging;
+ delete $cmd_cache{$cmd_name} ;
+ }
+
+ my @searched_in ;
+
+ ## This next bit is Unix/Win32 specific, unfortunately.
+ ## There's been some conversation about extending File::Spec to provide
+ ## a universal interface to PATH, but I haven't seen it yet.
+ my $re = Win32_MODE ? qr/;/ : qr/:/ ;
+
+LOOP:
+ for ( split( $re, $ENV{PATH}, -1 ) ) {
+ $_ = "." unless length $_ ;
+ push @searched_in, $_ ;
+
+ my $prospect = File::Spec->catfile( $_, $cmd_name ) ;
+ my @prospects ;
+
+ @prospects =
+ ( Win32_MODE && ! ( -f $prospect && -x _ ) )
+ ? map "$prospect$_", split /;/, $ENV{PATHEXT} || ".COM;.BAT;.EXE"
+ : ( $prospect ) ;
+
+ for my $found ( @prospects ) {
+ if ( -f $found && -x _ ) {
+ $cmd_cache{$cmd_name} = $found ;
+ last LOOP ;
+ }
+ }
+ }
+
+ if ( exists $cmd_cache{$cmd_name} ) {
+ _debug "'", $cmd_name, "' added to cache: '", $cmd_cache{$cmd_name}, "'"
+ if _debugging_details ;
+ return $cmd_cache{$cmd_name} ;
+ }
+
+ croak "Command '$cmd_name' not found in " . join( ", ", @searched_in ) ;
+}
+
+
+sub _empty($) { ! ( defined $_[0] && length $_[0] ) }
+
+## 'safe' versions of otherwise fun things to do. See also IPC::Run::Win32Helper.
+sub _close {
+ confess 'undef' unless defined $_[0] ;
+ no strict 'refs' ;
+ my $fd = $_[0] =~ /^\d+$/ ? $_[0] : fileno $_[0] ;
+ my $r = POSIX::close $fd ;
+ $r = $r ? '' : " ERROR $!" ;
+ delete $fds{$fd} ;
+ _debug "close( $fd ) = " . ( $r || 0 ) if _debugging_details ;
+}
+
+sub _dup {
+ confess 'undef' unless defined $_[0] ;
+ my $r = POSIX::dup( $_[0] ) ;
+ croak "$!: dup( $_[0] )" unless defined $r ;
+ $r = 0 if $r eq '0 but true' ;
+ _debug "dup( $_[0] ) = $r" if _debugging_details ;
+ $fds{$r} = 1 ;
+ return $r ;
+}
+
+
+sub _dup2_rudely {
+ confess 'undef' unless defined $_[0] && defined $_[1] ;
+ my $r = POSIX::dup2( $_[0], $_[1] ) ;
+ croak "$!: dup2( $_[0], $_[1] )" unless defined $r ;
+ $r = 0 if $r eq '0 but true' ;
+ _debug "dup2( $_[0], $_[1] ) = $r" if _debugging_details ;
+ $fds{$r} = 1 ;
+ return $r ;
+}
+
+sub _exec {
+ confess 'undef passed' if grep !defined, @_ ;
+# exec @_ or croak "$!: exec( " . join( ', ', @_ ) . " )" ;
+ _debug 'exec()ing ', join " ", map "'$_'", @_ if _debugging_details ;
+
+# {
+## Commented out since we don't call this on Win32.
+# # This works around the bug where 5.6.1 complains
+# # "Can't exec ...: No error" after an exec on NT, where
+# # exec() is simulated and actually returns in Perl's C
+# # code, though Perl's &exec does not...
+# no warnings "exec" ;
+#
+# # Just in case the no warnings workaround
+# # stops beign a workaround, we don't want
+# # old values of $! causing spurious strerr()
+# # messages to appear in the "Can't exec" message
+# undef $! ;
+ exec @_ ;
+# }
+# croak "$!: exec( " . join( ', ', map "'$_'", @_ ) . " )" ;
+ ## Fall through so $! can be reported to parent.
+}
+
+
+sub _sysopen {
+ confess 'undef' unless defined $_[0] && defined $_[1] ;
+_debug sprintf( "O_RDONLY=0x%02x ", O_RDONLY ),
+sprintf( "O_WRONLY=0x%02x ", O_WRONLY ),
+sprintf( "O_RDWR=0x%02x ", O_RDWR ),
+sprintf( "O_TRUNC=0x%02x ", O_TRUNC),
+sprintf( "O_CREAT=0x%02x ", O_CREAT),
+sprintf( "O_APPEND=0x%02x ", O_APPEND),
+if _debugging_details ;
+ my $r = POSIX::open( $_[0], $_[1], 0644 ) ;
+ croak "$!: open( $_[0], ", sprintf( "0x%03x", $_[1] ), " )" unless defined $r ;
+ _debug "open( $_[0], ", sprintf( "0x%03x", $_[1] ), " ) = $r"
+ if _debugging_data ;
+ $fds{$r} = 1 ;
+ return $r ;
+}
+
+sub _pipe {
+ ## Normal, blocking write for pipes that we read and the child writes,
+ ## since most children expect writes to stdout to block rather than
+ ## do a partial write.
+ my ( $r, $w ) = POSIX::pipe ;
+ croak "$!: pipe()" unless defined $r ;
+ _debug "pipe() = ( $r, $w ) " if _debugging_details ;
+ $fds{$r} = $fds{$w} = 1 ;
+ return ( $r, $w ) ;
+}
+
+sub _pipe_nb {
+ ## For pipes that we write, unblock the write side, so we can fill a buffer
+ ## and continue to select().
+ ## Contributed by Borislav Deianov <borislav@ensim.com>, with minor
+ ## bugfix on fcntl result by me.
+ local ( *R, *W ) ;
+ my $f = pipe( R, W ) ;
+ croak "$!: pipe()" unless defined $f ;
+ my ( $r, $w ) = ( fileno R, fileno W ) ;
+ _debug "pipe_nb pipe() = ( $r, $w )" if _debugging_details ;
+ unless ( Win32_MODE ) {
+ ## POSIX::fcntl doesn't take fd numbers, so gotta use Perl's and
+ ## then _dup the originals (which get closed on leaving this block)
+ my $fres = fcntl( W, &F_SETFL, O_WRONLY | O_NONBLOCK );
+ croak "$!: fcntl( $w, F_SETFL, O_NONBLOCK )" unless $fres ;
+ _debug "fcntl( $w, F_SETFL, O_NONBLOCK )" if _debugging_details ;
+ }
+ ( $r, $w ) = ( _dup( $r ), _dup( $w ) ) ;
+ _debug "pipe_nb() = ( $r, $w )" if _debugging_details ;
+ return ( $r, $w ) ;
+}
+
+sub _pty {
+ require IO::Pty ;
+ my $pty = IO::Pty->new() ;
+ croak "$!: pty ()" unless $pty ;
+ $pty->autoflush() ;
+ $pty->blocking( 0 ) or croak "$!: pty->blocking ( 0 )" ;
+ _debug "pty() = ( ", $pty->fileno, ", ", $pty->slave->fileno, " )"
+ if _debugging_details ;
+ $fds{$pty->fileno} = $fds{$pty->slave->fileno} = 1 ;
+ return $pty ;
+}
+
+
+sub _read {
+ confess 'undef' unless defined $_[0] ;
+ my $s = '' ;
+ my $r = POSIX::read( $_[0], $s, 10_000 ) ;
+ croak "$!: read( $_[0] )" if not($r) and $! != POSIX::EINTR;
+ $r ||= 0;
+ _debug "read( $_[0] ) = $r chars '$s'" if _debugging_data ;
+ return $s ;
+}
+
+
+## A METHOD, not a function.
+sub _spawn {
+ my IPC::Run $self = shift ;
+ my ( $kid ) = @_ ;
+
+ _debug "opening sync pipe ", $kid->{PID} if _debugging_details ;
+ my $sync_reader_fd ;
+ ( $sync_reader_fd, $self->{SYNC_WRITER_FD} ) = _pipe ;
+ $kid->{PID} = fork() ;
+ croak "$! during fork" unless defined $kid->{PID} ;
+
+ unless ( $kid->{PID} ) {
+ ## _do_kid_and_exit closes sync_reader_fd since it closes all unwanted and
+ ## unloved fds.
+ $self->_do_kid_and_exit( $kid ) ;
+ }
+ _debug "fork() = ", $kid->{PID} if _debugging_details ;
+
+ ## Wait for kid to get to it's exec() and see if it fails.
+ _close $self->{SYNC_WRITER_FD} ;
+ my $sync_pulse = _read $sync_reader_fd ;
+ _close $sync_reader_fd ;
+
+ if ( ! defined $sync_pulse || length $sync_pulse ) {
+ if ( waitpid( $kid->{PID}, 0 ) >= 0 ) {
+ $kid->{RESULT} = $? ;
+ }
+ else {
+ $kid->{RESULT} = -1 ;
+ }
+ $sync_pulse =
+ "error reading synchronization pipe for $kid->{NUM}, pid $kid->{PID}"
+ unless length $sync_pulse ;
+ croak $sync_pulse ;
+ }
+ return $kid->{PID} ;
+
+## Wait for pty to get set up. This is a hack until we get synchronous
+## selects.
+if ( keys %{$self->{PTYS}} && $IO::Pty::VERSION < 0.9 ) {
+_debug "sleeping to give pty a chance to init, will fix when newer IO::Pty arrives." ;
+sleep 1 ;
+}
+}
+
+
+sub _write {
+ confess 'undef' unless defined $_[0] && defined $_[1] ;
+ my $r = POSIX::write( $_[0], $_[1], length $_[1] ) ;
+ croak "$!: write( $_[0], '$_[1]' )" unless $r ;
+ _debug "write( $_[0], '$_[1]' ) = $r" if _debugging_data ;
+ return $r ;
+}
+
+
+=item run
+
+Run takes a harness or harness specification and runs it, pumping
+all input to the child(ren), closing the input pipes when no more
+input is available, collecting all output that arrives, until the
+pipes delivering output are closed, then waiting for the children to
+exit and reaping their result codes.
+
+You may think of C<run( ... )> as being like
+
+ start( ... )->finish() ;
+
+, though there is one subtle difference: run() does not
+set \$input_scalars to '' like finish() does. If an exception is thrown
+from run(), all children will be killed off "gently", and then "annihilated"
+if they do not go gently (in to that dark night. sorry).
+
+If any exceptions are thrown, this does a L</kill_kill> before propogating
+them.
+
+=cut
+
+use vars qw( $in_run ); ## No, not Enron ;)
+
+sub run {
+ local $in_run = 1; ## Allow run()-only optimizations.
+ my IPC::Run $self = start( @_ );
+ my $r = eval {
+ $self->{clear_ins} = 0 ;
+ $self->finish ;
+ } ;
+ if ( $@ ) {
+ my $x = $@ ;
+ $self->kill_kill ;
+ die $x ;
+ }
+ return $r ;
+}
+
+
+=item signal
+
+ ## To send it a specific signal by name ("USR1"):
+ signal $h, "USR1" ;
+ $h->signal ( "USR1" ) ;
+
+If $signal is provided and defined, sends a signal to all child processes. Try
+not to send numeric signals, use C<"KILL"> instead of C<9>, for instance.
+Numeric signals aren't portable.
+
+Throws an exception if $signal is undef.
+
+This will I<not> clean up the harness, C<finish> it if you kill it.
+
+Normally TERM kills a process gracefully (this is what the command line utility
+C<kill> does by default), INT is sent by one of the keys C<^C>, C<Backspace> or
+C<E<lt>DelE<gt>>, and C<QUIT> is used to kill a process and make it coredump.
+
+The C<HUP> signal is often used to get a process to "restart", rereading
+config files, and C<USR1> and C<USR2> for really application-specific things.
+
+Often, running C<kill -l> (that's a lower case "L") on the command line will
+list the signals present on your operating system.
+
+B<WARNING>: The signal subsystem is not at all portable. We *may* offer
+to simulate C<TERM> and C<KILL> on some operating systems, submit code
+to me if you want this.
+
+B<WARNING 2>: Up to and including perl v5.6.1, doing almost anything in a
+signal handler could be dangerous. The most safe code avoids all
+mallocs and system calls, usually by preallocating a flag before
+entering the signal handler, altering the flag's value in the
+handler, and responding to the changed value in the main system:
+
+ my $got_usr1 = 0 ;
+ sub usr1_handler { ++$got_signal }
+
+ $SIG{USR1} = \&usr1_handler ;
+ while () { sleep 1 ; print "GOT IT" while $got_usr1-- ; }
+
+Even this approach is perilous if ++ and -- aren't atomic on your system
+(I've never heard of this on any modern CPU large enough to run perl).
+
+=cut
+
+sub signal {
+ my IPC::Run $self = shift ;
+
+ local $cur_self = $self ;
+
+ $self->_kill_kill_kill_pussycat_kill unless @_ ;
+
+ Carp::cluck "Ignoring extra parameters passed to kill()" if @_ > 1 ;
+
+ my ( $signal ) = @_ ;
+ croak "Undefined signal passed to signal" unless defined $signal ;
+ for ( grep $_->{PID} && ! defined $_->{RESULT}, @{$self->{KIDS}} ) {
+ _debug "sending $signal to $_->{PID}"
+ if _debugging;
+ kill $signal, $_->{PID}
+ or _debugging && _debug "$! sending $signal to $_->{PID}" ;
+ }
+
+ return ;
+}
+
+
+=item kill_kill
+
+ ## To kill off a process:
+ $h->kill_kill ;
+ kill_kill $h ;
+
+ ## To specify the grace period other than 30 seconds:
+ kill_kill $h, grace => 5 ;
+
+ ## To send QUIT instead of KILL if a process refuses to die:
+ kill_kill $h, coup_d_grace => "QUIT" ;
+
+Sends a C<TERM>, waits for all children to exit for up to 30 seconds, then
+sends a C<KILL> to any that survived the C<TERM>.
+
+Will wait for up to 30 more seconds for the OS to sucessfully C<KILL> the
+processes.
+
+The 30 seconds may be overriden by setting the C<grace> option, this
+overrides both timers.
+
+The harness is then cleaned up.
+
+The doubled name indicates that this function may kill again and avoids
+colliding with the core Perl C<kill> function.
+
+Returns a 1 if the C<TERM> was sufficient, or a 0 if C<KILL> was
+required. Throws an exception if C<KILL> did not permit the children
+to be reaped.
+
+B<NOTE>: The grace period is actually up to 1 second longer than that
+given. This is because the granularity of C<time> is 1 second. Let me
+know if you need finer granularity, we can leverage Time::HiRes here.
+
+B<Win32>: Win32 does not know how to send real signals, so C<TERM> is
+a full-force kill on Win32. Thus all talk of grace periods, etc. do
+not apply to Win32.
+
+=cut
+
+sub kill_kill {
+ my IPC::Run $self = shift ;
+
+ my %options = @_ ;
+ my $grace = $options{grace} ;
+ $grace = 30 unless defined $grace ;
+ ++$grace ; ## Make grace time a _minimum_
+
+ my $coup_d_grace = $options{coup_d_grace} ;
+ $coup_d_grace = "KILL" unless defined $coup_d_grace ;
+
+ delete $options{$_} for qw( grace coup_d_grace ) ;
+ Carp::cluck "Ignoring unknown options for kill_kill: ",
+ join " ",keys %options
+ if keys %options ;
+
+ $self->signal( "TERM" ) ;
+
+ my $quitting_time = time + $grace ;
+ my $delay = 0.01 ;
+ my $accum_delay ;
+
+ my $have_killed_before ;
+
+ while () {
+ ## delay first to yeild to other processes
+ select undef, undef, undef, $delay ;
+ $accum_delay += $delay ;
+
+ $self->reap_nb ;
+ last unless $self->_running_kids ;
+
+ if ( $accum_delay >= $grace*0.8 ) {
+ ## No point in checking until delay has grown some.
+ if ( time >= $quitting_time ) {
+ if ( ! $have_killed_before ) {
+ $self->signal( $coup_d_grace ) ;
+ $have_killed_before = 1 ;
+ $quitting_time += $grace ;
+ $delay = 0.01 ;
+ $accum_delay = 0 ;
+ next ;
+ }
+ croak "Unable to reap all children, even after KILLing them"
+ }
+ }
+
+ $delay *= 2 ;
+ $delay = 0.5 if $delay >= 0.5 ;
+ }
+
+ $self->_cleanup ;
+ return $have_killed_before ;
+}
+
+
+=item harness
+
+Takes a harness specification and returns a harness. This harness is
+blessed in to IPC::Run, allowing you to use method call syntax for
+run(), start(), et al if you like.
+
+harness() is provided so that you can pre-build harnesses if you
+would like to, but it's not required..
+
+You may proceed to run(), start() or pump() after calling harness() (pump()
+calls start() if need be). Alternatively, you may pass your
+harness specification to run() or start() and let them harness() for
+you. You can't pass harness specifications to pump(), though.
+
+=cut
+
+##
+## Notes: I've avoided handling a scalar that doesn't look like an
+## opcode as a here document or as a filename, though I could DWIM
+## those. I'm not sure that the advantages outweight the danger when
+## the DWIMer guesses wrong.
+##
+## TODO: allow user to spec default shell. Hmm, globally, in the
+## lexical scope hash, or per instance? 'Course they can do that
+## now by using a [...] to hold the command.
+##
+my $harness_id = 0 ;
+sub harness {
+ my $options ;
+ if ( @_ && ref $_[-1] eq 'HASH' ) {
+ $options = pop ;
+ require Data::Dumper ;
+ carp "Passing in options as a hash is deprecated:\n", Data::Dumper::Dumper( $options ) ;
+ }
+
+# local $IPC::Run::debug = $options->{debug}
+# if $options && defined $options->{debug} ;
+
+ my @args ;
+
+ if ( @_ == 1 && ! ref $_[0] ) {
+ if ( Win32_MODE ) {
+ @args = ( [ qw( command /c ), win32_parse_cmd_line $_[0] ] ) ;
+ }
+ else {
+ @args = ( [ qw( sh -c ), @_ ] ) ;
+ }
+ }
+ elsif ( @_ > 1 && ! grep ref $_, @_ ) {
+ @args = ( [ @_ ] ) ;
+ }
+ else {
+ @args = @_ ;
+ }
+
+ my @errs ; # Accum errors, emit them when done.
+
+ my $succinct ; # set if no redir ops are required yet. Cleared
+ # if an op is seen.
+
+ my $cur_kid ; # references kid or handle being parsed
+
+ my $assumed_fd = 0 ; # fd to assume in succinct mode (no redir ops)
+ my $handle_num = 0 ; # 1... is which handle we're parsing
+
+ my IPC::Run $self = bless {}, __PACKAGE__;
+
+ local $cur_self = $self ;
+
+ $self->{ID} = ++$harness_id ;
+ $self->{IOS} = [] ;
+ $self->{KIDS} = [] ;
+ $self->{PIPES} = [] ;
+ $self->{PTYS} = {} ;
+ $self->{STATE} = _newed ;
+
+ if ( $options ) {
+ $self->{$_} = $options->{$_}
+ for keys %$options ;
+ }
+
+ _debug "****** harnessing *****" if _debugging;
+
+ my $first_parse ;
+ local $_ ;
+ my $arg_count = @args ;
+ while ( @args ) { for ( shift @args ) {
+ eval {
+ $first_parse = 1 ;
+ _debug(
+ "parsing ",
+ defined $_
+ ? ref $_ eq 'ARRAY'
+ ? ( '[ ', join( ', ', map "'$_'", @$_ ), ' ]' )
+ : ( ref $_
+ || ( length $_ < 50
+ ? "'$_'"
+ : join( '', "'", substr( $_, 0, 10 ), "...'" )
+ )
+ )
+ : '<undef>'
+ ) if _debugging;
+
+ REPARSE:
+ if ( ref eq 'ARRAY' || ( ! $cur_kid && ref eq 'CODE' ) ) {
+ croak "Process control symbol ('|', '&') missing" if $cur_kid ;
+ croak "Can't spawn a subroutine on Win32"
+ if Win32_MODE && ref eq "CODE" ;
+ $cur_kid = {
+ TYPE => 'cmd',
+ VAL => $_,
+ NUM => @{$self->{KIDS}} + 1,
+ OPS => [],
+ PID => '',
+ RESULT => undef,
+ } ;
+ push @{$self->{KIDS}}, $cur_kid ;
+ $succinct = 1 ;
+ }
+
+ elsif ( isa( $_, 'IPC::Run::IO' ) ) {
+ push @{$self->{IOS}}, $_ ;
+ $cur_kid = undef ;
+ $succinct = 1 ;
+ }
+
+ elsif ( isa( $_, 'IPC::Run::Timer' ) ) {
+ push @{$self->{TIMERS}}, $_ ;
+ $cur_kid = undef ;
+ $succinct = 1 ;
+ }
+
+ elsif ( /^(\d*)>&(\d+)$/ ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'dup',
+ KFD1 => $2,
+ KFD2 => length $1 ? $1 : 1,
+ } ;
+ _debug "redirect operators now required" if _debugging_details ;
+ $succinct = ! $first_parse ;
+ }
+
+ elsif ( /^(\d*)<&(\d+)$/ ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'dup',
+ KFD1 => $2,
+ KFD2 => length $1 ? $1 : 0,
+ } ;
+ $succinct = ! $first_parse ;
+ }
+
+ elsif ( /^(\d*)<&-$/ ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'close',
+ KFD => length $1 ? $1 : 0,
+ } ;
+ $succinct = ! $first_parse ;
+ }
+
+ elsif (
+ /^(\d*) (<pipe)() () () $/x
+ || /^(\d*) (<pty) ((?:\s+\S+)?) (<) () $/x
+ || /^(\d*) (<) () () (.*)$/x
+ ) {
+ croak "No command before '$_'" unless $cur_kid ;
+
+ $succinct = ! $first_parse ;
+
+ my $type = $2 . $4 ;
+
+ my $kfd = length $1 ? $1 : 0 ;
+
+ my $pty_id ;
+ if ( $type eq '<pty<' ) {
+ $pty_id = length $3 ? $3 : '0' ;
+ ## do the require here to cause early error reporting
+ require IO::Pty ;
+ ## Just flag the pyt's existence for now. It'll be
+ ## converted to a real IO::Pty by _open_pipes.
+ $self->{PTYS}->{$pty_id} = undef ;
+ }
+
+ my $source = $5 ;
+
+ my @filters ;
+ my $binmode ;
+
+ unless ( length $source ) {
+ if ( ! $succinct ) {
+ while ( @args > 1
+ && (
+ ( ref $args[1] && ! isa $args[1], "IPC::Run::Timer" )
+ || isa $args[0], "IPC::Run::binmode_pseudo_filter"
+ )
+ ) {
+ if ( isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
+ $binmode = shift( @args )->() ;
+ }
+ else {
+ push @filters, shift @args
+ }
+ }
+ }
+ $source = shift @args ;
+ croak "'$_' missing a source" if _empty $source ;
+
+ _debug(
+ 'Kid ', $cur_kid->{NUM}, "'s input fd ", $kfd,
+ ' has ', scalar( @filters ), ' filters.'
+ ) if _debugging_details && @filters ;
+ } ;
+
+ my IPC::Run::IO $pipe = IPC::Run::IO->_new_internal(
+ $type, $kfd, $pty_id, $source, $binmode, @filters
+ ) ;
+
+ if ( ( ref $source eq 'GLOB' || isa $source, 'IO::Handle' )
+ && $type !~ /^<p(ty<|ipe)$/
+ ) {
+ _debug "setting DONT_CLOSE" if _debugging_details ;
+ $pipe->{DONT_CLOSE} = 1 ; ## this FD is not closed by us.
+ _dont_inherit( $source ) if Win32_MODE ;
+ }
+
+ push @{$cur_kid->{OPS}}, $pipe ;
+ }
+
+ elsif ( /^() (>>?) (&) () (.*)$/x
+ || /^() (&) (>pipe) () () $/x
+ || /^() (>pipe)(&) () () $/x
+ || /^(\d*)() (>pipe) () () $/x
+ || /^() (&) (>pty) ( \w*)> () $/x
+## TODO: || /^() (>pty) (\d*)> (&) () $/x
+ || /^(\d*)() (>pty) ( \w*)> () $/x
+ || /^() (&) (>>?) () (.*)$/x
+ || /^(\d*)() (>>?) () (.*)$/x
+ ) {
+ croak "No command before '$_'" unless $cur_kid ;
+
+ $succinct = ! $first_parse ;
+
+ my $type = (
+ $2 eq '>pipe' || $3 eq '>pipe'
+ ? '>pipe'
+ : $2 eq '>pty' || $3 eq '>pty'
+ ? '>pty>'
+ : '>'
+ ) ;
+ my $kfd = length $1 ? $1 : 1 ;
+ my $trunc = ! ( $2 eq '>>' || $3 eq '>>' ) ;
+ my $pty_id = (
+ $2 eq '>pty' || $3 eq '>pty'
+ ? length $4 ? $4 : 0
+ : undef
+ ) ;
+
+ my $stderr_too =
+ $2 eq '&'
+ || $3 eq '&'
+ || ( ! length $1 && substr( $type, 0, 4 ) eq '>pty' ) ;
+
+ my $dest = $5 ;
+ my @filters ;
+ my $binmode = 0 ;
+ unless ( length $dest ) {
+ if ( ! $succinct ) {
+ ## unshift...shift: '>' filters source...sink left...right
+ while ( @args > 1
+ && (
+ ( ref $args[1] && ! isa $args[1], "IPC::Run::Timer" )
+ || isa $args[0], "IPC::Run::binmode_pseudo_filter"
+ )
+ ) {
+ if ( isa $args[0], "IPC::Run::binmode_pseudo_filter" ) {
+ $binmode = shift( @args )->() ;
+ }
+ else {
+ unshift @filters, shift @args ;
+ }
+ }
+ }
+
+ $dest = shift @args ;
+
+ _debug(
+ 'Kid ', $cur_kid->{NUM}, "'s output fd ", $kfd,
+ ' has ', scalar( @filters ), ' filters.'
+ ) if _debugging_details && @filters ;
+
+ if ( $type eq '>pty>' ) {
+ ## do the require here to cause early error reporting
+ require IO::Pty ;
+ ## Just flag the pyt's existence for now. _open_pipes()
+ ## will new an IO::Pty for each key.
+ $self->{PTYS}->{$pty_id} = undef ;
+ }
+ }
+
+ croak "'$_' missing a destination" if _empty $dest ;
+ my $pipe = IPC::Run::IO->_new_internal(
+ $type, $kfd, $pty_id, $dest, $binmode, @filters
+ ) ;
+ $pipe->{TRUNC} = $trunc ;
+
+ if ( ( isa( $dest, 'GLOB' ) || isa( $dest, 'IO::Handle' ) )
+ && $type !~ /^>(pty>|pipe)$/
+ ) {
+ _debug "setting DONT_CLOSE" if _debugging_details ;
+ $pipe->{DONT_CLOSE} = 1 ; ## this FD is not closed by us.
+ }
+ push @{$cur_kid->{OPS}}, $pipe ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'dup',
+ KFD1 => 1,
+ KFD2 => 2,
+ } if $stderr_too ;
+ }
+
+ elsif ( $_ eq "|" ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ unshift @{$cur_kid->{OPS}}, {
+ TYPE => '|',
+ KFD => 1,
+ } ;
+ $succinct = 1 ;
+ $assumed_fd = 1 ;
+ $cur_kid = undef ;
+ }
+
+ elsif ( $_ eq "&" ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ unshift @{$cur_kid->{OPS}}, {
+ TYPE => 'close',
+ KFD => 0,
+ } ;
+ $succinct = 1 ;
+ $assumed_fd = 0 ;
+ $cur_kid = undef ;
+ }
+
+ elsif ( $_ eq 'init' ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'init',
+ SUB => shift @args,
+ } ;
+ }
+
+ elsif ( ! ref $_ ) {
+ $self->{$_} = shift @args;
+ }
+
+ elsif ( $_ eq 'init' ) {
+ croak "No command before '$_'" unless $cur_kid ;
+ push @{$cur_kid->{OPS}}, {
+ TYPE => 'init',
+ SUB => shift @args,
+ } ;
+ }
+
+ elsif ( $succinct && $first_parse ) {
+ ## It's not an opcode, and no explicit opcodes have been
+ ## seen yet, so assume it's a file name.
+ unshift @args, $_ ;
+ if ( ! $assumed_fd ) {
+ $_ = "$assumed_fd<",
+ }
+ else {
+ $_ = "$assumed_fd>",
+ }
+ _debug "assuming '", $_, "'" if _debugging_details ;
+ ++$assumed_fd ;
+ $first_parse = 0 ;
+ goto REPARSE ;
+ }
+
+ else {
+ croak join(
+ '',
+ 'Unexpected ',
+ ( ref() ? $_ : 'scalar' ),
+ ' in harness() parameter ',
+ $arg_count - @args
+ ) ;
+ }
+ } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ } }
+
+ die join( '', @errs ) if @errs ;
+
+
+ $self->{STATE} = _harnessed ;
+# $self->timeout( $options->{timeout} ) if exists $options->{timeout} ;
+ return $self ;
+}
+
+
+sub _open_pipes {
+ my IPC::Run $self = shift ;
+
+ my @errs ;
+
+ my @close_on_fail ;
+
+ ## When a pipe character is seen, a pipe is created. $pipe_read_fd holds
+ ## the dangling read end of the pipe until we get to the next process.
+ my $pipe_read_fd ;
+
+ ## Output descriptors for the last command are shared by all children.
+ ## @output_fds_accum accumulates the current set of output fds.
+ my @output_fds_accum ;
+
+ for ( sort keys %{$self->{PTYS}} ) {
+ _debug "opening pty '", $_, "'" if _debugging_details ;
+ my $pty = _pty ;
+ $self->{PTYS}->{$_} = $pty ;
+ }
+
+ for ( @{$self->{IOS}} ) {
+ eval { $_->init ; } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ else {
+ push @close_on_fail, $_ ;
+ }
+ }
+
+ ## Loop through the kids and their OPS, interpreting any that require
+ ## parent-side actions.
+ for my $kid ( @{$self->{KIDS}} ) {
+ unless ( ref $kid->{VAL} eq 'CODE' ) {
+ $kid->{PATH} = _search_path $kid->{VAL}->[0] ;
+ }
+ if ( defined $pipe_read_fd ) {
+ _debug "placing write end of pipe on kid $kid->{NUM}'s stdin"
+ if _debugging_details ;
+ unshift @{$kid->{OPS}}, {
+ TYPE => 'PIPE', ## Prevent next loop from triggering on this
+ KFD => 0,
+ TFD => $pipe_read_fd,
+ } ;
+ $pipe_read_fd = undef ;
+ }
+ @output_fds_accum = () ;
+ for my $op ( @{$kid->{OPS}} ) {
+# next if $op->{IS_DEBUG} ;
+ my $ok = eval {
+ if ( $op->{TYPE} eq '<' ) {
+ my $source = $op->{SOURCE};
+ if ( ! ref $source ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to read ", $op->{KFD},
+ " from '" . $source, "' (read only)"
+ ) if _debugging_details ;
+ croak "simulated open failure"
+ if $self->{_simulate_open_failure} ;
+ $op->{TFD} = _sysopen( $source, O_RDONLY ) ;
+ push @close_on_fail, $op->{TFD} ;
+ }
+ elsif ( isa( $source, 'GLOB' )
+ || isa( $source, 'IO::Handle' )
+ ) {
+ croak
+ "Unopened filehandle in input redirect for $op->{KFD}"
+ unless defined fileno $source ;
+ $op->{TFD} = fileno $source ;
+ _debug(
+ "kid ", $kid->{NUM}, " to read ", $op->{KFD},
+ " from fd ", $op->{TFD}
+ ) if _debugging_details ;
+ }
+ elsif ( isa( $source, 'SCALAR' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to read ", $op->{KFD},
+ " from SCALAR"
+ ) if _debugging_details ;
+
+ $op->open_pipe( $self->_debug_fd ) ;
+ push @close_on_fail, $op->{KFD}, $op->{FD} ;
+
+ my $s = '' ;
+ $op->{KIN_REF} = \$s ;
+ }
+ elsif ( isa( $source, 'CODE' ) ) {
+ _debug(
+ 'kid ', $kid->{NUM}, ' to read ', $op->{KFD}, ' from CODE'
+ ) if _debugging_details ;
+
+ $op->open_pipe( $self->_debug_fd ) ;
+ push @close_on_fail, $op->{KFD}, $op->{FD} ;
+
+ my $s = '' ;
+ $op->{KIN_REF} = \$s ;
+ }
+ else {
+ croak(
+ "'"
+ . ref( $source )
+ . "' not allowed as a source for input redirection"
+ ) ;
+ }
+ $op->_init_filters ;
+ }
+ elsif ( $op->{TYPE} eq '<pipe' ) {
+ _debug(
+ 'kid to read ', $op->{KFD},
+ ' from a pipe IPC::Run opens and returns',
+ ) if _debugging_details ;
+
+ my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{SOURCE} ) ;
+ _debug "caller will write to ", fileno $op->{SOURCE}
+ if _debugging_details;
+
+ $op->{TFD} = $r ;
+ $op->{FD} = undef ; # we don't manage this fd
+ $op->_init_filters ;
+ }
+ elsif ( $op->{TYPE} eq '<pty<' ) {
+ _debug(
+ 'kid to read ', $op->{KFD}, " from pty '", $op->{PTY_ID}, "'",
+ ) if _debugging_details ;
+
+ for my $source ( $op->{SOURCE} ) {
+ if ( isa( $source, 'SCALAR' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to read ", $op->{KFD},
+ " from SCALAR via pty '", $op->{PTY_ID}, "'"
+ ) if _debugging_details ;
+
+ my $s = '' ;
+ $op->{KIN_REF} = \$s ;
+ }
+ elsif ( isa( $source, 'CODE' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to read ", $op->{KFD},
+ " from CODE via pty '", $op->{PTY_ID}, "'"
+ ) if _debugging_details ;
+ my $s = '' ;
+ $op->{KIN_REF} = \$s ;
+ }
+ else {
+ croak(
+ "'"
+ . ref( $source )
+ . "' not allowed as a source for '<pty<' redirection"
+ ) ;
+ }
+ }
+ $op->{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno ;
+ $op->{TFD} = undef ; # The fd isn't known until after fork().
+ $op->_init_filters ;
+ }
+ elsif ( $op->{TYPE} eq '>' ) {
+ ## N> output redirection.
+ my $dest = $op->{DEST} ;
+ if ( ! ref $dest ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to write ", $op->{KFD},
+ " to '", $dest, "' (write only, create, ",
+ ( $op->{TRUNC} ? 'truncate' : 'append' ),
+ ")"
+ ) if _debugging_details ;
+ croak "simulated open failure"
+ if $self->{_simulate_open_failure} ;
+ $op->{TFD} = _sysopen(
+ $dest,
+ ( O_WRONLY
+ | O_CREAT
+ | ( $op->{TRUNC} ? O_TRUNC : O_APPEND )
+ )
+ ) ;
+ if ( Win32_MODE ) {
+ ## I have no idea why this is needed to make the current
+ ## file position survive the gyrations TFD must go
+ ## through...
+ POSIX::lseek( $op->{TFD}, 0, POSIX::SEEK_END() ) ;
+ }
+ push @close_on_fail, $op->{TFD} ;
+ }
+ elsif ( isa( $dest, 'GLOB' ) ) {
+ croak(
+ "Unopened filehandle in output redirect, command $kid->{NUM}"
+ ) unless defined fileno $dest ;
+ ## Turn on autoflush, mostly just to flush out
+ ## existing output.
+ my $old_fh = select( $dest ) ; $| = 1 ; select( $old_fh ) ;
+ $op->{TFD} = fileno $dest ;
+ _debug(
+ 'kid to write ', $op->{KFD}, ' to handle ', $op->{TFD}
+ ) if _debugging_details ;
+ }
+ elsif ( isa( $dest, 'SCALAR' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to write $op->{KFD} to SCALAR"
+ ) if _debugging_details ;
+
+ $op->open_pipe( $self->_debug_fd ) ;
+ push @close_on_fail, $op->{FD}, $op->{TFD} ;
+ $$dest = '' if $op->{TRUNC} ;
+ }
+ elsif ( isa( $dest, 'CODE' ) ) {
+ _debug(
+ "kid $kid->{NUM} to write $op->{KFD} to CODE"
+ ) if _debugging_details ;
+
+ $op->open_pipe( $self->_debug_fd ) ;
+ push @close_on_fail, $op->{FD}, $op->{TFD} ;
+ }
+ else {
+ croak(
+ "'"
+ . ref( $dest )
+ . "' not allowed as a sink for output redirection"
+ ) ;
+ }
+ $output_fds_accum[$op->{KFD}] = $op ;
+ $op->_init_filters ;
+ }
+
+ elsif ( $op->{TYPE} eq '>pipe' ) {
+ ## N> output redirection to a pipe we open, but don't select()
+ ## on.
+ _debug(
+ "kid ", $kid->{NUM}, " to write ", $op->{KFD},
+ ' to a pipe IPC::Run opens and returns'
+ ) if _debugging_details ;
+
+ my ( $r, $w ) = $op->open_pipe( $self->_debug_fd, $op->{DEST} ) ;
+ _debug "caller will read from ", fileno $op->{DEST}
+ if _debugging_details ;
+
+ $op->{TFD} = $w ;
+ $op->{FD} = undef ; # we don't manage this fd
+ $op->_init_filters ;
+
+ $output_fds_accum[$op->{KFD}] = $op ;
+ }
+ elsif ( $op->{TYPE} eq '>pty>' ) {
+ my $dest = $op->{DEST} ;
+ if ( isa( $dest, 'SCALAR' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to write ", $op->{KFD},
+ " to SCALAR via pty '", $op->{PTY_ID}, "'"
+ ) if _debugging_details ;
+
+ $$dest = '' if $op->{TRUNC} ;
+ }
+ elsif ( isa( $dest, 'CODE' ) ) {
+ _debug(
+ "kid ", $kid->{NUM}, " to write ", $op->{KFD},
+ " to CODE via pty '", $op->{PTY_ID}, "'"
+ ) if _debugging_details ;
+ }
+ else {
+ croak(
+ "'"
+ . ref( $dest )
+ . "' not allowed as a sink for output redirection"
+ ) ;
+ }
+
+ $op->{FD} = $self->{PTYS}->{$op->{PTY_ID}}->fileno ;
+ $op->{TFD} = undef ; # The fd isn't known until after fork().
+ $output_fds_accum[$op->{KFD}] = $op ;
+ $op->_init_filters ;
+ }
+ elsif ( $op->{TYPE} eq '|' ) {
+ _debug(
+ "pipelining $kid->{NUM} and "
+ . ( $kid->{NUM} + 1 )
+ ) if _debugging_details ;
+ ( $pipe_read_fd, $op->{TFD} ) = _pipe ;
+ if ( Win32_MODE ) {
+ _dont_inherit( $pipe_read_fd ) ;
+ _dont_inherit( $op->{TFD} ) ;
+ }
+ @output_fds_accum = () ;
+ }
+ elsif ( $op->{TYPE} eq '&' ) {
+ @output_fds_accum = () ;
+ } # end if $op->{TYPE} tree
+ 1;
+ } ; # end eval
+ unless ( $ok ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ } # end for ( OPS }
+ }
+
+ if ( @errs ) {
+ for ( @close_on_fail ) {
+ _close( $_ ) ;
+ $_ = undef ;
+ }
+ for ( keys %{$self->{PTYS}} ) {
+ next unless $self->{PTYS}->{$_} ;
+ close $self->{PTYS}->{$_} ;
+ $self->{PTYS}->{$_} = undef ;
+ }
+ die join( '', @errs )
+ }
+
+ ## give all but the last child all of the output file descriptors
+ ## These will be reopened (and thus rendered useless) if the child
+ ## dup2s on to these descriptors, since we unshift these. This way
+ ## each process emits output to the same file descriptors that the
+ ## last child will write to. This is probably not quite correct,
+ ## since each child should write to the file descriptors inherited
+ ## from the parent.
+ ## TODO: fix the inheritance of output file descriptors.
+ ## NOTE: This sharing of OPS among kids means that we can't easily put
+ ## a kid number in each OPS structure to ping the kid when all ops
+ ## have closed (when $self->{PIPES} has emptied). This means that we
+ ## need to scan the KIDS whenever @{$self->{PIPES}} is empty to see
+ ## if there any of them are still alive.
+ for ( my $num = 0 ; $num < $#{$self->{KIDS}} ; ++$num ) {
+ for ( reverse @output_fds_accum ) {
+ next unless defined $_ ;
+ _debug(
+ 'kid ', $self->{KIDS}->[$num]->{NUM}, ' also to write ', $_->{KFD},
+ ' to ', ref $_->{DEST}
+ ) if _debugging_details ;
+ unshift @{$self->{KIDS}->[$num]->{OPS}}, $_ ;
+ }
+ }
+
+ ## Open the debug pipe if we need it
+ ## Create the list of PIPES we need to scan and the bit vectors needed by
+ ## select(). Do this first so that _cleanup can _clobber() them if an
+ ## exception occurs.
+ @{$self->{PIPES}} = () ;
+ $self->{RIN} = '' ;
+ $self->{WIN} = '' ;
+ $self->{EIN} = '' ;
+ ## PIN is a vec()tor that indicates who's paused.
+ $self->{PIN} = '' ;
+ for my $kid ( @{$self->{KIDS}} ) {
+ for ( @{$kid->{OPS}} ) {
+ if ( defined $_->{FD} ) {
+ _debug(
+ 'kid ', $kid->{NUM}, '[', $kid->{PID}, "]'s ", $_->{KFD},
+ ' is my ', $_->{FD}
+ ) if _debugging_details ;
+ vec( $self->{ $_->{TYPE} =~ /^</ ? 'WIN' : 'RIN' }, $_->{FD}, 1 ) = 1 ;
+# vec( $self->{EIN}, $_->{FD}, 1 ) = 1 ;
+ push @{$self->{PIPES}}, $_ ;
+ }
+ }
+ }
+
+ for my $io ( @{$self->{IOS}} ) {
+ my $fd = $io->fileno ;
+ vec( $self->{RIN}, $fd, 1 ) = 1 if $io->mode =~ /r/ ;
+ vec( $self->{WIN}, $fd, 1 ) = 1 if $io->mode =~ /w/ ;
+# vec( $self->{EIN}, $fd, 1 ) = 1 ;
+ push @{$self->{PIPES}}, $io ;
+ }
+
+ ## Put filters on the end of the filter chains to read & write the pipes.
+ ## Clear pipe states
+ for my $pipe ( @{$self->{PIPES}} ) {
+ $pipe->{SOURCE_EMPTY} = 0 ;
+ $pipe->{PAUSED} = 0 ;
+ if ( $pipe->{TYPE} =~ /^>/ ) {
+ my $pipe_reader = sub {
+ my ( undef, $out_ref ) = @_ ;
+
+ return undef unless defined $pipe->{FD} ;
+ return 0 unless vec( $self->{ROUT}, $pipe->{FD}, 1 ) ;
+
+ vec( $self->{ROUT}, $pipe->{FD}, 1 ) = 0 ;
+
+ _debug_desc_fd( 'reading from', $pipe ) if _debugging_details ;
+ my $in = eval { _read( $pipe->{FD} ) } ;
+ if ( $@ ) {
+ $in = '' ;
+ ## IO::Pty throws the Input/output error if the kid dies.
+ ## read() throws the bad file descriptor message if the
+ ## kid dies on Win32.
+ die $@ unless
+ $@ =~ /^Input\/output error: read/ ||
+ ($@ =~ /input or output/ && $^O =~ /aix/)
+ || ( Win32_MODE && $@ =~ /Bad file descriptor/ ) ;
+ }
+
+ unless ( length $in ) {
+ $self->_clobber( $pipe ) ;
+ return undef ;
+ }
+
+ ## Protect the position so /.../g matches may be used.
+ my $pos = pos $$out_ref ;
+ $$out_ref .= $in ;
+ pos( $$out_ref ) = $pos ;
+ return 1 ;
+ } ;
+ ## Input filters are the last filters
+ push @{$pipe->{FILTERS}}, $pipe_reader ;
+ push @{$self->{TEMP_FILTERS}}, $pipe_reader ;
+ }
+ else {
+ my $pipe_writer = sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+ return undef unless defined $pipe->{FD} ;
+ return 0
+ unless vec( $self->{WOUT}, $pipe->{FD}, 1 )
+ || $pipe->{PAUSED} ;
+
+ vec( $self->{WOUT}, $pipe->{FD}, 1 ) = 0 ;
+
+ if ( ! length $$in_ref ) {
+ if ( ! defined get_more_input ) {
+ $self->_clobber( $pipe ) ;
+ return undef ;
+ }
+ }
+
+ unless ( length $$in_ref ) {
+ unless ( $pipe->{PAUSED} ) {
+ _debug_desc_fd( 'pausing', $pipe ) if _debugging_details ;
+ vec( $self->{WIN}, $pipe->{FD}, 1 ) = 0 ;
+# vec( $self->{EIN}, $pipe->{FD}, 1 ) = 0 ;
+ vec( $self->{PIN}, $pipe->{FD}, 1 ) = 1 ;
+ $pipe->{PAUSED} = 1 ;
+ }
+ return 0 ;
+ }
+ _debug_desc_fd( 'writing to', $pipe ) if _debugging_details ;
+
+ my $c = _write( $pipe->{FD}, $$in_ref ) ;
+ substr( $$in_ref, 0, $c, '' ) ;
+ return 1 ;
+ } ;
+ ## Output filters are the first filters
+ unshift @{$pipe->{FILTERS}}, $pipe_writer ;
+ push @{$self->{TEMP_FILTERS}}, $pipe_writer ;
+ }
+ }
+}
+
+
+sub _dup2_gently {
+ ## A METHOD, NOT A FUNCTION, NEEDS $self!
+ my IPC::Run $self = shift ;
+ my ( $files, $fd1, $fd2 ) = @_ ;
+ ## Moves TFDs that are using the destination fd out of the
+ ## way before calling _dup2
+ for ( @$files ) {
+ next unless defined $_->{TFD} ;
+ $_->{TFD} = _dup( $_->{TFD} ) if $_->{TFD} == $fd2 ;
+ }
+ $self->{DEBUG_FD} = _dup $self->{DEBUG_FD}
+ if defined $self->{DEBUG_FD} && $self->{DEBUG_FD} == $fd2 ;
+
+ _dup2_rudely( $fd1, $fd2 ) ;
+}
+
+=item close_terminal
+
+This is used as (or in) an init sub to cast off the bonds of a controlling
+terminal. It must precede all other redirection ops that affect
+STDIN, STDOUT, or STDERR to be guaranteed effective.
+
+=cut
+
+
+sub close_terminal {
+ ## Cast of the bonds of a controlling terminal
+
+ POSIX::setsid() || croak "POSIX::setsid() failed" ;
+ _debug "closing stdin, out, err"
+ if _debugging_details ;
+ close STDIN ;
+ close STDERR ;
+ close STDOUT ;
+}
+
+
+sub _do_kid_and_exit {
+ my IPC::Run $self = shift ;
+ my ( $kid ) = @_ ;
+
+ ## For unknown reasons, placing these two statements in the eval{}
+ ## causes the eval {} to not catch errors after they are executed in
+ ## perl 5.6.0, godforsaken version that it is...not sure about 5.6.1.
+ ## Part of this could be that these symbols get destructed when
+ ## exiting the eval, and that destruction might be what's (wrongly)
+ ## confusing the eval{}, allowing the exception to probpogate.
+ my $s1 = gensym ;
+ my $s2 = gensym ;
+
+ eval {
+ local $cur_self = $self ;
+
+ _set_child_debug_name( ref $kid->{VAL} eq "CODE"
+ ? "CODE"
+ : basename( $kid->{VAL}->[0] )
+ );
+
+ ## close parent FD's first so they're out of the way.
+ ## Don't close STDIN, STDOUT, STDERR: they should be inherited or
+ ## overwritten below.
+ my @needed = $self->{noinherit} ? () : ( 1, 1, 1 ) ;
+ $needed[ $self->{SYNC_WRITER_FD} ] = 1 ;
+ $needed[ $self->{DEBUG_FD} ] = 1 if defined $self->{DEBUG_FD} ;
+
+ for ( @{$kid->{OPS}} ) {
+ $needed[ $_->{TFD} ] = 1 if defined $_->{TFD} ;
+ }
+
+ ## TODO: use the forthcoming IO::Pty to close the terminal and
+ ## make the first pty for this child the controlling terminal.
+ ## This will also make it so that pty-laden kids don't cause
+ ## other kids to lose stdin/stdout/stderr.
+ my @closed ;
+ if ( %{$self->{PTYS}} ) {
+ ## Clean up the parent's fds.
+ for ( keys %{$self->{PTYS}} ) {
+ _debug "Cleaning up parent's ptty '$_'" if _debugging_details ;
+ my $slave = $self->{PTYS}->{$_}->slave ;
+ $closed[ $self->{PTYS}->{$_}->fileno ] = 1 ;
+ close $self->{PTYS}->{$_} ;
+ $self->{PTYS}->{$_} = $slave ;
+ }
+
+ close_terminal ;
+ $closed[ $_ ] = 1 for ( 0..2 ) ;
+ }
+
+ for my $sibling ( @{$self->{KIDS}} ) {
+ for ( @{$sibling->{OPS}} ) {
+ if ( $_->{TYPE} =~ /^.pty.$/ ) {
+ $_->{TFD} = $self->{PTYS}->{$_->{PTY_ID}}->fileno ;
+ $needed[$_->{TFD}] = 1 ;
+ }
+
+# for ( $_->{FD}, ( $sibling != $kid ? $_->{TFD} : () ) ) {
+# if ( defined $_ && ! $closed[$_] && ! $needed[$_] ) {
+# _close( $_ ) ;
+# $closed[$_] = 1 ;
+# $_ = undef ;
+# }
+# }
+ }
+ }
+
+ ## This is crude: we have no way of keeping track of browsing all open
+ ## fds, so we scan to a fairly high fd.
+ _debug "open fds: ", join " ", keys %fds if _debugging_details ;
+ for (keys %fds) {
+ if ( ! $closed[$_] && ! $needed[$_] ) {
+ _close( $_ ) ;
+ $closed[$_] = 1 ;
+ }
+ }
+
+ ## Lazy closing is so the same fd (ie the same TFD value) can be dup2'ed on
+ ## several times.
+ my @lazy_close ;
+ for ( @{$kid->{OPS}} ) {
+ if ( defined $_->{TFD} ) {
+ unless ( $_->{TFD} == $_->{KFD} ) {
+ $self->_dup2_gently( $kid->{OPS}, $_->{TFD}, $_->{KFD} ) ;
+ push @lazy_close, $_->{TFD} ;
+ }
+ }
+ elsif ( $_->{TYPE} eq 'dup' ) {
+ $self->_dup2_gently( $kid->{OPS}, $_->{KFD1}, $_->{KFD2} )
+ unless $_->{KFD1} == $_->{KFD2} ;
+ }
+ elsif ( $_->{TYPE} eq 'close' ) {
+ for ( $_->{KFD} ) {
+ if ( ! $closed[$_] ) {
+ _close( $_ ) ;
+ $closed[$_] = 1 ;
+ $_ = undef ;
+ }
+ }
+ }
+ elsif ( $_->{TYPE} eq 'init' ) {
+ $_->{SUB}->() ;
+ }
+ }
+
+ for ( @lazy_close ) {
+ unless ( $closed[$_] ) {
+ _close( $_ ) ;
+ $closed[$_] = 1 ;
+ }
+ }
+
+ if ( ref $kid->{VAL} ne 'CODE' ) {
+ open $s1, ">&=$self->{SYNC_WRITER_FD}"
+ or croak "$! setting filehandle to fd SYNC_WRITER_FD" ;
+ fcntl $s1, F_SETFD, 1 ;
+
+ if ( defined $self->{DEBUG_FD} ) {
+ open $s2, ">&=$self->{DEBUG_FD}"
+ or croak "$! setting filehandle to fd DEBUG_FD" ;
+ fcntl $s2, F_SETFD, 1 ;
+ }
+
+ my @cmd = ( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ) ;
+ _debug 'execing ', join " ", map { /[\s"]/ ? "'$_'" : $_ } @cmd
+ if _debugging ;
+
+ die "exec failed: simulating exec() failure"
+ if $self->{_simulate_exec_failure} ;
+
+ _exec $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ;
+
+ croak "exec failed: $!" ;
+ }
+ } ;
+ if ( $@ ) {
+ _write $self->{SYNC_WRITER_FD}, $@ ;
+ ## Avoid DESTROY.
+ POSIX::exit 1 ;
+ }
+
+ ## We must be executing code in the child, otherwise exec() would have
+ ## prevented us from being here.
+ _close $self->{SYNC_WRITER_FD} ;
+ _debug 'calling fork()ed CODE ref' if _debugging;
+ POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
+ ## TODO: Overload CORE::GLOBAL::exit...
+ $kid->{VAL}->() ;
+
+ ## There are bugs in perl closures up to and including 5.6.1
+ ## that may keep this next line from having any effect, and it
+ ## won't have any effect if our caller has kept a copy of it, but
+ ## this may cause the closure to be cleaned up. Maybe.
+ $kid->{VAL} = undef ;
+
+ ## Use POSIX::exit to avoid global destruction, since this might
+ ## cause DESTROY() to be called on objects created in the parent
+ ## and thus cause double cleanup. For instance, if DESTROY() unlinks
+ ## a file in the child, we don't want the parent to suddenly miss
+ ## it.
+ POSIX::exit 0 ;
+}
+
+
+=item start
+
+ $h = start(
+ \@cmd, \$in, \$out, ...,
+ timeout( 30, name => "process timeout" ),
+ $stall_timeout = timeout( 10, name => "stall timeout" ),
+ ) ;
+
+ $h = start \@cmd, '<', \$in, '|', \@cmd2, ... ;
+
+start() accepts a harness or harness specification and returns a harness
+after building all of the pipes and launching (via fork()/exec(), or, maybe
+someday, spawn()) all the child processes. It does not send or receive any
+data on the pipes, see pump() and finish() for that.
+
+You may call harness() and then pass it's result to start() if you like,
+but you only need to if it helps you structure or tune your application.
+If you do call harness(), you may skip start() and proceed directly to
+pump.
+
+start() also starts all timers in the harness. See L<IPC::Run::Timer>
+for more information.
+
+start() flushes STDOUT and STDERR to help you avoid duplicate output.
+It has no way of asking Perl to flush all your open filehandles, so
+you are going to need to flush any others you have open. Sorry.
+
+Here's how if you don't want to alter the state of $| for your
+filehandle:
+
+ $ofh = select HANDLE ; $of = $| ; $| = 1 ; $| = $of ; select $ofh;
+
+If you don't mind leaving output unbuffered on HANDLE, you can do
+the slightly shorter
+
+ $ofh = select HANDLE ; $| = 1 ; select $ofh;
+
+Or, you can use IO::Handle's flush() method:
+
+ use IO::Handle ;
+ flush HANDLE ;
+
+Perl needs the equivalent of C's fflush( (FILE *)NULL ).
+
+=cut
+
+sub start {
+# $SIG{__DIE__} = sub { my $s = shift ; Carp::cluck $s ; die $s } ;
+ my $options ;
+ if ( @_ && ref $_[-1] eq 'HASH' ) {
+ $options = pop ;
+ require Data::Dumper ;
+ carp "Passing in options as a hash is deprecated:\n", Data::Dumper::Dumper( $options ) ;
+ }
+
+ my IPC::Run $self ;
+ if ( @_ == 1 && isa( $_[0], __PACKAGE__ ) ) {
+ $self = shift ;
+ $self->{$_} = $options->{$_} for keys %$options ;
+ }
+ else {
+ $self = harness( @_, $options ? $options : () ) ;
+ }
+
+ local $cur_self = $self ;
+
+ $self->kill_kill if $self->{STATE} == _started ;
+
+ _debug "** starting" if _debugging;
+
+ $_->{RESULT} = undef for @{$self->{KIDS}} ;
+
+ ## Assume we're not being called from &run. It will correct our
+ ## assumption if need be. This affects whether &_select_loop clears
+ ## input queues to '' when they're empty.
+ $self->{clear_ins} = 1 ;
+
+ IPC::Run::Win32Helper::optimize $self
+ if Win32_MODE && $in_run;
+
+ my @errs ;
+
+ for ( @{$self->{TIMERS}} ) {
+ eval { $_->start } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ }
+
+ eval { $self->_open_pipes } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+
+ if ( ! @errs ) {
+ ## This is a bit of a hack, we should do it for all open filehandles.
+ ## Since there's no way I know of to enumerate open filehandles, we
+ ## autoflush STDOUT and STDERR. This is done so that the children don't
+ ## inherit output buffers chock full o' redundant data. It's really
+ ## confusing to track that down.
+ { my $ofh = select STDOUT ; local $| = 1 ; select $ofh; }
+ { my $ofh = select STDERR ; local $| = 1 ; select $ofh; }
+ for my $kid ( @{$self->{KIDS}} ) {
+ $kid->{RESULT} = undef ;
+ _debug "child: ",
+ ref( $kid->{VAL} ) eq "CODE"
+ ? "CODE ref"
+ : (
+ "`",
+ join( " ", map /[^\w.-]/ ? "'$_'" : $_, @{$kid->{VAL}} ),
+ "`"
+ ) if _debugging_details ;
+ eval {
+ croak "simulated failure of fork"
+ if $self->{_simulate_fork_failure} ;
+ unless ( Win32_MODE ) {
+ $self->_spawn( $kid ) ;
+ }
+ else {
+## TODO: Test and debug spawing code. Someday.
+ _debug(
+ 'spawning ',
+ join(
+ ' ',
+ map(
+ "'$_'",
+ ( $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] )
+ )
+ )
+ ) if _debugging;
+ ## The external kid wouldn't know what to do with it anyway.
+ ## This is only used by the "helper" pump processes on Win32.
+ _dont_inherit( $self->{DEBUG_FD} ) ;
+ ( $kid->{PID}, $kid->{PROCESS} ) =
+ IPC::Run::Win32Helper::win32_spawn(
+ [ $kid->{PATH}, @{$kid->{VAL}}[1..$#{$kid->{VAL}}] ],
+ $kid->{OPS},
+ ) ;
+ _debug "spawn() = ", $kid->{PID} if _debugging;
+ }
+ } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ }
+ }
+
+ ## Close all those temporary filehandles that the kids needed.
+ for my $pty ( values %{$self->{PTYS}} ) {
+ close $pty->slave ;
+ }
+
+ my @closed ;
+ for my $kid ( @{$self->{KIDS}} ) {
+ for ( @{$kid->{OPS}} ) {
+ my $close_it = eval {
+ defined $_->{TFD}
+ && ! $_->{DONT_CLOSE}
+ && ! $closed[$_->{TFD}]
+ && ( ! Win32_MODE || ! $_->{RECV_THROUGH_TEMP_FILE} ) ## Win32 hack
+ } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ if ( $close_it || $@ ) {
+ eval {
+ _close( $_->{TFD} ) ;
+ $closed[$_->{TFD}] = 1 ;
+ $_->{TFD} = undef ;
+ } ;
+ if ( $@ ) {
+ push @errs, $@ ;
+ _debug 'caught ', $@ if _debugging;
+ }
+ }
+ }
+ }
+confess "gak!" unless defined $self->{PIPES} ;
+
+ if ( @errs ) {
+ eval { $self->_cleanup } ;
+ warn $@ if $@ ;
+ die join( '', @errs ) ;
+ }
+
+ $self->{STATE} = _started ;
+ return $self ;
+}
+
+
+sub adopt {
+ ## NOT FUNCTIONAL YET, NEED TO CLOSE FDS BETTER IN CHILDREN. SEE
+ ## t/adopt.t for a test suite.
+ my IPC::Run $self = shift ;
+
+ for my $adoptee ( @_ ) {
+ push @{$self->{IOS}}, @{$adoptee->{IOS}} ;
+ ## NEED TO RENUMBER THE KIDS!!
+ push @{$self->{KIDS}}, @{$adoptee->{KIDS}} ;
+ push @{$self->{PIPES}}, @{$adoptee->{PIPES}} ;
+ $self->{PTYS}->{$_} = $adoptee->{PTYS}->{$_}
+ for keys %{$adoptee->{PYTS}} ;
+ push @{$self->{TIMERS}}, @{$adoptee->{TIMERS}} ;
+ $adoptee->{STATE} = _finished ;
+ }
+}
+
+
+sub _clobber {
+ my IPC::Run $self = shift ;
+ my ( $file ) = @_ ;
+ _debug_desc_fd( "closing", $file ) if _debugging_details ;
+ my $doomed = $file->{FD} ;
+ my $dir = $file->{TYPE} =~ /^</ ? 'WIN' : 'RIN' ;
+ vec( $self->{$dir}, $doomed, 1 ) = 0 ;
+# vec( $self->{EIN}, $doomed, 1 ) = 0 ;
+ vec( $self->{PIN}, $doomed, 1 ) = 0 ;
+ if ( $file->{TYPE} =~ /^(.)pty.$/ ) {
+ if ( $1 eq '>' ) {
+ ## Only close output ptys. This is so that ptys as inputs are
+ ## never autoclosed, which would risk losing data that was
+ ## in the slave->parent queue.
+ _debug_desc_fd "closing pty", $file if _debugging_details ;
+ close $self->{PTYS}->{$file->{PTY_ID}}
+ if defined $self->{PTYS}->{$file->{PTY_ID}} ;
+ $self->{PTYS}->{$file->{PTY_ID}} = undef ;
+ }
+ }
+ elsif ( isa( $file, 'IPC::Run::IO' ) ) {
+ $file->close unless $file->{DONT_CLOSE} ;
+ }
+ else {
+ _close( $doomed ) ;
+ }
+
+ @{$self->{PIPES}} = grep
+ defined $_->{FD} && ( $_->{TYPE} ne $file->{TYPE} || $_->{FD} ne $doomed),
+ @{$self->{PIPES}} ;
+
+ $file->{FD} = undef ;
+}
+
+sub _select_loop {
+ my IPC::Run $self = shift ;
+
+ my $io_occurred ;
+
+ my $not_forever = 0.01 ;
+
+SELECT:
+ while ( $self->pumpable ) {
+ if ( $io_occurred && $self->{break_on_io} ) {
+ _debug "exiting _select(): io occured and break_on_io set"
+ if _debugging_details ;
+ last ;
+ }
+
+ my $timeout = $self->{non_blocking} ? 0 : undef ;
+
+ if ( @{$self->{TIMERS}} ) {
+ my $now = time ;
+ my $time_left ;
+ for ( @{$self->{TIMERS}} ) {
+ next unless $_->is_running ;
+ $time_left = $_->check( $now ) ;
+ ## Return when a timer expires
+ return if defined $time_left && ! $time_left ;
+ $timeout = $time_left
+ if ! defined $timeout || $time_left < $timeout ;
+ }
+ }
+
+ ##
+ ## See if we can unpause any input channels
+ ##
+ my $paused = 0 ;
+
+ for my $file ( @{$self->{PIPES}} ) {
+ next unless $file->{PAUSED} && $file->{TYPE} =~ /^</ ;
+
+ _debug_desc_fd( "checking for more input", $file ) if _debugging_details ;
+ my $did ;
+ 1 while $did = $file->_do_filters( $self ) ;
+ if ( defined $file->{FD} && ! defined( $did ) || $did ) {
+ _debug_desc_fd( "unpausing", $file ) if _debugging_details ;
+ $file->{PAUSED} = 0 ;
+ vec( $self->{WIN}, $file->{FD}, 1 ) = 1 ;
+# vec( $self->{EIN}, $file->{FD}, 1 ) = 1 ;
+ vec( $self->{PIN}, $file->{FD}, 1 ) = 0 ;
+ }
+ else {
+ ## This gets incremented occasionally when the IO channel
+ ## was actually closed. That's a bug, but it seems mostly
+ ## harmless: it causes us to exit if break_on_io, or to set
+ ## the timeout to not be forever. I need to fix it, though.
+ ++$paused ;
+ }
+ }
+
+ if ( _debugging_details ) {
+ my $map = join(
+ '',
+ map {
+ my $out ;
+ $out = 'r' if vec( $self->{RIN}, $_, 1 ) ;
+ $out = $out ? 'b' : 'w' if vec( $self->{WIN}, $_, 1 ) ;
+ $out = 'p' if ! $out && vec( $self->{PIN}, $_, 1 ) ;
+ $out = $out ? uc( $out ) : 'x' if vec( $self->{EIN}, $_, 1 ) ;
+ $out = '-' unless $out ;
+ $out ;
+ } (0..1024)
+ ) ;
+ $map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/ ;
+ _debug 'fds for select: ', $map if _debugging_details ;
+ }
+
+ ## _do_filters may have closed our last fd, and we need to see if
+ ## we have I/O, or are just waiting for children to exit.
+ my $p = $self->pumpable;
+ last unless $p;
+ if ( $p > 0 && ( ! defined $timeout || $timeout > 0.1 ) ) {
+ ## No I/O will wake the select loop up, but we have children
+ ## lingering, so we need to poll them with a short timeout.
+ ## Otherwise, assume more input will be coming.
+ $timeout = $not_forever ;
+ $not_forever *= 2 ;
+ $not_forever = 0.5 if $not_forever >= 0.5 ;
+ }
+
+ ## Make sure we don't block forever in select() because inputs are
+ ## paused.
+ if ( ! defined $timeout && ! ( @{$self->{PIPES}} - $paused ) ) {
+ ## Need to return if we're in pump and all input is paused, or
+ ## we'll loop until all inputs are unpaused, which is darn near
+ ## forever. And a day.
+ if ( $self->{break_on_io} ) {
+ _debug "exiting _select(): no I/O to do and timeout=forever"
+ if _debugging;
+ last ;
+ }
+
+ ## Otherwise, assume more input will be coming.
+ $timeout = $not_forever ;
+ $not_forever *= 2 ;
+ $not_forever = 0.5 if $not_forever >= 0.5 ;
+ }
+
+ _debug 'timeout=', defined $timeout ? $timeout : 'forever'
+ if _debugging_details ;
+
+ my $nfound ;
+ unless ( Win32_MODE ) {
+ $nfound = select(
+ $self->{ROUT} = $self->{RIN},
+ $self->{WOUT} = $self->{WIN},
+ $self->{EOUT} = $self->{EIN},
+ $timeout
+ ) ;
+ }
+ else {
+ my @in = map $self->{$_}, qw( RIN WIN EIN ) ;
+ ## Win32's select() on Win32 seems to die if passed vectors of
+ ## all 0's. Need to report this when I get back online.
+ for ( @in ) {
+ $_ = undef unless index( ( unpack "b*", $_ ), 1 ) >= 0 ;
+ }
+
+ $nfound = select(
+ $self->{ROUT} = $in[0],
+ $self->{WOUT} = $in[1],
+ $self->{EOUT} = $in[2],
+ $timeout
+ ) ;
+
+ for ( $self->{ROUT}, $self->{WOUT}, $self->{EOUT} ) {
+ $_ = "" unless defined $_ ;
+ }
+ }
+ last if ! $nfound && $self->{non_blocking} ;
+
+ croak "$! in select" if $nfound < 0 and $! != POSIX::EINTR;
+ ## TODO: Analyze the EINTR failure mode and see if this patch
+ ## is adequate and optimal.
+ ## TODO: Add an EINTR test to the test suite.
+
+ if ( _debugging_details ) {
+ my $map = join(
+ '',
+ map {
+ my $out ;
+ $out = 'r' if vec( $self->{ROUT}, $_, 1 ) ;
+ $out = $out ? 'b' : 'w' if vec( $self->{WOUT}, $_, 1 ) ;
+ $out = $out ? uc( $out ) : 'x' if vec( $self->{EOUT}, $_, 1 ) ;
+ $out = '-' unless $out ;
+ $out ;
+ } (0..128)
+ ) ;
+ $map =~ s/((?:[a-zA-Z-]|\([^\)]*\)){12,}?)-*$/$1/ ;
+ _debug "selected ", $map ;
+ }
+
+ ## Need to copy since _clobber alters @{$self->{PIPES}}.
+ ## TODO: Rethink _clobber(). Rethink $file->{PAUSED}, too.
+ my @pipes = @{$self->{PIPES}} ;
+ $io_occurred = $_->poll( $self ) ? 1 : $io_occurred for @pipes;
+# FILE:
+# for my $pipe ( @pipes ) {
+# ## Pipes can be shared among kids. If another kid closes the
+# ## pipe, then it's {FD} will be undef. Also, on Win32, pipes can
+# ## be optimized to be files, in which case the FD is left undef
+# ## so we don't try to select() on it.
+# if ( $pipe->{TYPE} =~ /^>/
+# && defined $pipe->{FD}
+# && vec( $self->{ROUT}, $pipe->{FD}, 1 )
+# ) {
+# _debug_desc_fd( "filtering data from", $pipe ) if _debugging_details ;
+#confess "phooey" unless isa( $pipe, "IPC::Run::IO" ) ;
+# $io_occurred = 1 if $pipe->_do_filters( $self ) ;
+#
+# next FILE unless defined $pipe->{FD} ;
+# }
+#
+# ## On Win32, pipes to the child can be optimized to be files
+# ## and FD left undefined so we won't select on it.
+# if ( $pipe->{TYPE} =~ /^</
+# && defined $pipe->{FD}
+# && vec( $self->{WOUT}, $pipe->{FD}, 1 )
+# ) {
+# _debug_desc_fd( "filtering data to", $pipe ) if _debugging_details ;
+# $io_occurred = 1 if $pipe->_do_filters( $self ) ;
+#
+# next FILE unless defined $pipe->{FD} ;
+# }
+#
+# if ( defined $pipe->{FD} && vec( $self->{EOUT}, $pipe->{FD}, 1 ) ) {
+# ## BSD seems to sometimes raise the exceptional condition flag
+# ## when a pipe is closed before we read it's last data. This
+# ## causes spurious warnings and generally renders the exception
+# ## mechanism useless for our purposes. The exception
+# ## flag semantics are too variable (they're device driver
+# ## specific) for me to easily map to any automatic action like
+# ## warning or croaking (try running v0.42 if you don't beleive me
+# ## :-).
+# warn "Exception on descriptor $pipe->{FD}" ;
+# }
+# }
+ }
+
+ return ;
+}
+
+
+sub _cleanup {
+ my IPC::Run $self = shift ;
+ _debug "cleaning up" if _debugging_details ;
+
+ for ( values %{$self->{PTYS}} ) {
+ next unless ref $_ ;
+ eval {
+ _debug "closing slave fd ", fileno $_->slave if _debugging_data;
+ close $_->slave ;
+ } ;
+ carp $@ . " while closing ptys" if $@ ;
+ eval {
+ _debug "closing master fd ", fileno $_ if _debugging_data;
+ close $_ ;
+ } ;
+ carp $@ . " closing ptys" if $@ ;
+ }
+
+ _debug "cleaning up pipes" if _debugging_details ;
+ ## _clobber modifies PIPES
+ $self->_clobber( $self->{PIPES}->[0] ) while @{$self->{PIPES}} ;
+
+ for my $kid ( @{$self->{KIDS}} ) {
+ _debug "cleaning up kid ", $kid->{NUM} if _debugging_details ;
+ if ( ! length $kid->{PID} ) {
+ _debug 'never ran child ', $kid->{NUM}, ", can't reap"
+ if _debugging;
+ for my $op ( @{$kid->{OPS}} ) {
+ _close( $op->{TFD} )
+ if defined $op->{TFD} && ! defined $op->{TEMP_FILE_HANDLE};
+ }
+ }
+ elsif ( ! defined $kid->{RESULT} ) {
+ _debug 'reaping child ', $kid->{NUM}, ' (pid ', $kid->{PID}, ')'
+ if _debugging;
+ my $pid = waitpid $kid->{PID}, 0 ;
+ $kid->{RESULT} = $? ;
+ _debug 'reaped ', $pid, ', $?=', $kid->{RESULT}
+ if _debugging;
+ }
+
+# if ( defined $kid->{DEBUG_FD} ) {
+# die;
+# @{$kid->{OPS}} = grep
+# ! defined $_->{KFD} || $_->{KFD} != $kid->{DEBUG_FD},
+# @{$kid->{OPS}} ;
+# $kid->{DEBUG_FD} = undef ;
+# }
+
+ _debug "cleaning up filters" if _debugging_details ;
+ for my $op ( @{$kid->{OPS}} ) {
+ @{$op->{FILTERS}} = grep {
+ my $filter = $_ ;
+ ! grep $filter == $_, @{$self->{TEMP_FILTERS}} ;
+ } @{$op->{FILTERS}} ;
+ }
+
+ for my $op ( @{$kid->{OPS}} ) {
+ $op->_cleanup( $self ) if UNIVERSAL::isa( $op, "IPC::Run::IO" );
+ }
+ }
+ $self->{STATE} = _finished ;
+ @{$self->{TEMP_FILTERS}} = () ;
+ _debug "done cleaning up" if _debugging_details ;
+
+ POSIX::close $self->{DEBUG_FD} if defined $self->{DEBUG_FD} ;
+ $self->{DEBUG_FD} = undef ;
+}
+
+
+=item pump
+
+ pump $h ;
+ $h->pump ;
+
+Pump accepts a single parameter harness. It blocks until it delivers some
+input or recieves some output. It returns TRUE if there is still input or
+output to be done, FALSE otherwise.
+
+pump() will automatically call start() if need be, so you may call harness()
+then proceed to pump() if that helps you structure your application.
+
+If pump() is called after all harnessed activities have completed, a "process
+ended prematurely" exception to be thrown. This allows for simple scripting
+of external applications without having to add lots of error handling code at
+each step of the script:
+
+ $h = harness \@smbclient, \$in, \$out, $err ;
+
+ $in = "cd /foo\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/m ;
+ die "error cding to /foo:\n$out" if $out =~ "ERR" ;
+ $out = '' ;
+
+ $in = "mget *\n" ;
+ $h->pump until $out =~ /^smb.*> \Z/m ;
+ die "error retrieving files:\n$out" if $out =~ "ERR" ;
+
+ $h->finish ;
+
+ warn $err if $err ;
+
+=cut
+
+
+sub pump {
+ die "pump() takes only a a single harness as a parameter"
+ unless @_ == 1 && isa( $_[0], __PACKAGE__ ) ;
+
+ my IPC::Run $self = shift ;
+
+ local $cur_self = $self ;
+
+ _debug "** pumping"
+ if _debugging;
+
+# my $r = eval {
+ $self->start if $self->{STATE} < _started ;
+ croak "process ended prematurely" unless $self->pumpable ;
+
+ $self->{auto_close_ins} = 0 ;
+ $self->{break_on_io} = 1 ;
+ $self->_select_loop ;
+ return $self->pumpable ;
+# } ;
+# if ( $@ ) {
+# my $x = $@ ;
+# _debug $x if _debugging && $x ;
+# eval { $self->_cleanup } ;
+# warn $@ if $@ ;
+# die $x ;
+# }
+# return $r ;
+}
+
+
+=item pump_nb
+
+ pump_nb $h ;
+ $h->pump_nb ;
+
+"pump() non-blocking", pumps if anything's ready to be pumped, returns
+immediately otherwise. This is useful if you're doing some long-running
+task in the foreground, but don't want to starve any child processes.
+
+=cut
+
+sub pump_nb {
+ my IPC::Run $self = shift ;
+
+ $self->{non_blocking} = 1 ;
+ my $r = eval { $self->pump } ;
+ $self->{non_blocking} = 0 ;
+ die $@ if $@ ;
+ return $r ;
+}
+
+=item pumpable
+
+Returns TRUE if calling pump() won't throw an immediate "process ended
+prematurely" exception. This means that there are open I/O channels or
+active processes. May yield the parent processes' time slice for 0.01
+second if all pipes are to the child and all are paused. In this case
+we can't tell if the child is dead, so we yield the processor and
+then attempt to reap the child in a nonblocking way.
+
+=cut
+
+## Undocumented feature (don't depend on it outside this module):
+## returns -1 if we have I/O channels open, or >0 if no I/O channels
+## open, but we have kids running. This allows the select loop
+## to poll for child exit.
+sub pumpable {
+ my IPC::Run $self = shift ;
+
+ ## There's a catch-22 we can get in to if there is only one pipe left
+ ## open to the child and it's paused (ie the SCALAR it's tied to
+ ## is ''). It's paused, so we're not select()ing on it, so we don't
+ ## check it to see if the child attached to it is alive and it stays
+ ## in @{$self->{PIPES}} forever. So, if all pipes are paused, see if
+ ## we can reap the child.
+ return -1 if grep !$_->{PAUSED}, @{$self->{PIPES}};
+
+ ## See if the child is dead.
+ $self->reap_nb;
+ return 0 unless $self->_running_kids;
+
+ ## If we reap_nb and it's not dead yet, yield to it to see if it
+ ## exits.
+ ##
+ ## A better solution would be to unpause all the pipes, but I tried that
+ ## and it never errored on linux. Sigh.
+ select undef, undef, undef, 0.0001;
+
+ ## try again
+ $self->reap_nb ;
+ return 0 unless $self->_running_kids;
+
+ return -1; ## There are pipes waiting
+}
+
+
+sub _running_kids {
+ my IPC::Run $self = shift ;
+ return grep
+ defined $_->{PID} && ! defined $_->{RESULT},
+ @{$self->{KIDS}} ;
+}
+
+
+=item reap_nb
+
+Attempts to reap child processes, but does not block.
+
+Does not currently take any parameters, one day it will allow specific
+children to be reaped.
+
+Only call this from a signal handler if your C<perl> is recent enough
+to have safe signal handling (5.6.1 did not, IIRC, but it was beign discussed
+on perl5-porters). Calling this (or doing any significant work) in a signal
+handler on older C<perl>s is asking for seg faults.
+
+=cut
+
+my $still_runnings ;
+
+sub reap_nb {
+ my IPC::Run $self = shift ;
+
+ local $cur_self = $self ;
+
+ ## No more pipes, look to see if all the kids yet live, reaping those
+ ## that haven't. I'd use $SIG{CHLD}/$SIG{CLD}, but that's broken
+ ## on older (SYSV) platforms and perhaps less portable than waitpid().
+ ## This could be slow with a lot of kids, but that's rare and, well,
+ ## a lot of kids is slow in the first place.
+ ## Oh, and this keeps us from reaping other children the process
+ ## may have spawned.
+ for my $kid ( @{$self->{KIDS}} ) {
+ if ( Win32_MODE ) {
+ next if ! defined $kid->{PROCESS} || defined $kid->{RESULT} ;
+ unless ( $kid->{PROCESS}->Wait( 0 ) ) {
+ _debug "kid $kid->{NUM} ($kid->{PID}) still running"
+ if _debugging_details;
+ next ;
+ }
+
+ _debug "kid $kid->{NUM} ($kid->{PID}) exited"
+ if _debugging;
+
+ $kid->{PROCESS}->GetExitCode( $kid->{RESULT} )
+ or croak "$! while GetExitCode()ing for Win32 process" ;
+
+ unless ( defined $kid->{RESULT} ) {
+ $kid->{RESULT} = "0 but true" ;
+ $? = $kid->{RESULT} = 0x0F ;
+ }
+ else {
+ $? = $kid->{RESULT} << 8 ;
+ }
+ }
+ else {
+ next if ! defined $kid->{PID} || defined $kid->{RESULT} ;
+ my $pid = waitpid $kid->{PID}, POSIX::WNOHANG() ;
+ unless ( $pid ) {
+ _debug "$kid->{NUM} ($kid->{PID}) still running"
+ if _debugging_details;
+ next ;
+ }
+
+ if ( $pid < 0 ) {
+ _debug "No such process: $kid->{PID}\n" if _debugging ;
+ $kid->{RESULT} = "unknown result, unknown PID" ;
+ }
+ else {
+ _debug "kid $kid->{NUM} ($kid->{PID}) exited"
+ if _debugging;
+
+ confess "waitpid returned the wrong PID: $pid instead of $kid->{PID}"
+ unless $pid = $kid->{PID} ;
+ _debug "$kid->{PID} returned $?\n" if _debugging ;
+ $kid->{RESULT} = $? ;
+ }
+ }
+ }
+}
+
+
+=item finish
+
+This must be called after the last start() or pump() call for a harness,
+or your system will accumulate defunct processes and you may "leak"
+file descriptors.
+
+finish() returns TRUE if all children returned 0 (and were not signaled and did
+not coredump, ie ! $?), and FALSE otherwise (this is like run(), and the
+opposite of system()).
+
+Once a harness has been finished, it may be run() or start()ed again,
+including by pump()s auto-start.
+
+If this throws an exception rather than a normal exit, the harness may
+be left in an unstable state, it's best to kill the harness to get rid
+of all the child processes, etc.
+
+Specifically, if a timeout expires in finish(), finish() will not
+kill all the children. Call C<<$h->kill_kill>> in this case if you care.
+This differs from the behavior of L</run>.
+
+=cut
+
+
+sub finish {
+ my IPC::Run $self = shift ;
+ my $options = @_ && ref $_[-1] eq 'HASH' ? pop : {} ;
+
+ local $cur_self = $self ;
+
+ _debug "** finishing" if _debugging;
+
+ $self->{non_blocking} = 0 ;
+ $self->{auto_close_ins} = 1 ;
+ $self->{break_on_io} = 0 ;
+ # We don't alter $self->{clear_ins}, start() and run() control it.
+
+ while ( $self->pumpable ) {
+ $self->_select_loop( $options ) ;
+ }
+ $self->_cleanup ;
+
+ return ! $self->full_result ;
+}
+
+
+=item result
+
+ $h->result ;
+
+Returns the first non-zero result code (ie $? >> 8). See L</full_result> to
+get the $? value for a child process.
+
+To get the result of a particular child, do:
+
+ $h->result( 0 ) ; # first child's $? >> 8
+ $h->result( 1 ) ; # second child
+
+or
+
+ ($h->results)[0]
+ ($h->results)[1]
+
+Returns undef if no child processes were spawned and no child number was
+specified. Throws an exception if an out-of-range child number is passed.
+
+=cut
+
+sub _assert_finished {
+ my IPC::Run $self = $_[0] ;
+
+ croak "Harness not run" unless $self->{STATE} >= _finished ;
+ croak "Harness not finished running" unless $self->{STATE} == _finished ;
+}
+
+
+sub result {
+ &_assert_finished ;
+ my IPC::Run $self = shift ;
+
+ if ( @_ ) {
+ my ( $which ) = @_ ;
+ croak(
+ "Only ",
+ scalar( @{$self->{KIDS}} ),
+ " child processes, no process $which"
+ )
+ unless $which >= 0 && $which <= $#{$self->{KIDS}} ;
+ return $self->{KIDS}->[$which]->{RESULT} >> 8 ;
+ }
+ else {
+ return undef unless @{$self->{KIDS}} ;
+ for ( @{$self->{KIDS}} ) {
+ return $_->{RESULT} >> 8 if $_->{RESULT} >> 8 ;
+ }
+ }
+}
+
+
+=item results
+
+Returns a list of child exit values. See L</full_results> if you want to
+know if a signal killed the child.
+
+Throws an exception if the harness is not in a finished state.
+
+=cut
+
+sub results {
+ &_assert_finished ;
+ my IPC::Run $self = shift ;
+
+ # we add 0 here to stop warnings associated with "unknown result, unknown PID"
+ return map { (0+$_->{RESULT}) >> 8 } @{$self->{KIDS}} ;
+}
+
+
+=item full_result
+
+ $h->full_result ;
+
+Returns the first non-zero $?. See L</result> to get the first $? >> 8
+value for a child process.
+
+To get the result of a particular child, do:
+
+ $h->full_result( 0 ) ; # first child's $? >> 8
+ $h->full_result( 1 ) ; # second child
+
+or
+
+ ($h->full_results)[0]
+ ($h->full_results)[1]
+
+Returns undef if no child processes were spawned and no child number was
+specified. Throws an exception if an out-of-range child number is passed.
+
+=cut
+
+sub full_result {
+ goto &result if @_ > 1 ;
+ &_assert_finished ;
+
+ my IPC::Run $self = shift ;
+
+ return undef unless @{$self->{KIDS}} ;
+ for ( @{$self->{KIDS}} ) {
+ return $_->{RESULT} if $_->{RESULT} ;
+ }
+}
+
+
+=item full_results
+
+Returns a list of child exit values as returned by C<wait>. See L</results>
+if you don't care about coredumps or signals.
+
+Throws an exception if the harness is not in a finished state.
+
+=cut
+
+sub full_results {
+ &_assert_finished ;
+ my IPC::Run $self = shift ;
+
+ croak "Harness not run" unless $self->{STATE} >= _finished ;
+ croak "Harness not finished running" unless $self->{STATE} == _finished ;
+
+ return map $_->{RESULT}, @{$self->{KIDS}} ;
+}
+
+
+##
+## Filter Scaffolding
+##
+use vars (
+ '$filter_op', ## The op running a filter chain right now
+ '$filter_num', ## Which filter is being run right now.
+) ;
+
+##
+## A few filters and filter constructors
+##
+
+=back
+
+=head1 FILTERS
+
+These filters are used to modify input our output between a child
+process and a scalar or subroutine endpoint.
+
+=over
+
+=item binary
+
+ run \@cmd, ">", binary, \$out ;
+ run \@cmd, ">", binary, \$out ; ## Any TRUE value to enable
+ run \@cmd, ">", binary 0, \$out ; ## Any FALSE value to disable
+
+This is a constructor for a "binmode" "filter" that tells IPC::Run to keep
+the carriage returns that would ordinarily be edited out for you (binmode
+is usually off). This is not a real filter, but an option masquerading as
+a filter.
+
+It's not named "binmode" because you're likely to want to call Perl's binmode
+in programs that are piping binary data around.
+
+=cut
+
+sub binary(;$) {
+ my $enable = @_ ? shift : 1 ;
+ return bless sub { $enable }, "IPC::Run::binmode_pseudo_filter" ;
+}
+
+=item new_chunker
+
+This breaks a stream of data in to chunks, based on an optional
+scalar or regular expression parameter. The default is the Perl
+input record separator in $/, which is a newline be default.
+
+ run \@cmd, '>', new_chunker, \&lines_handler ;
+ run \@cmd, '>', new_chunker( "\r\n" ), \&lines_handler ;
+
+Because this uses $/ by default, you should always pass in a parameter
+if you are worried about other code (modules, etc) modifying $/.
+
+If this filter is last in a filter chain that dumps in to a scalar,
+the scalar must be set to '' before a new chunk will be written to it.
+
+As an example of how a filter like this can be written, here's a
+chunker that splits on newlines:
+
+ sub line_splitter {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return 0 if length $$out_ref ;
+
+ return input_avail && do {
+ while (1) {
+ if ( $$in_ref =~ s/\A(.*?\n)// ) {
+ $$out_ref .= $1 ;
+ return 1 ;
+ }
+ my $hmm = get_more_input ;
+ unless ( defined $hmm ) {
+ $$out_ref = $$in_ref ;
+ $$in_ref = '' ;
+ return length $$out_ref ? 1 : 0 ;
+ }
+ return 0 if $hmm eq 0 ;
+ }
+ }
+ } ;
+
+=cut
+
+sub new_chunker(;$) {
+ my ( $re ) = @_ ;
+ $re = $/ if _empty $re ;
+ $re = quotemeta( $re ) unless ref $re eq 'Regexp' ;
+ $re = qr/\A(.*?$re)/s ;
+
+ return sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return 0 if length $$out_ref ;
+
+ return input_avail && do {
+ while (1) {
+ if ( $$in_ref =~ s/$re// ) {
+ $$out_ref .= $1 ;
+ return 1 ;
+ }
+ my $hmm = get_more_input ;
+ unless ( defined $hmm ) {
+ $$out_ref = $$in_ref ;
+ $$in_ref = '' ;
+ return length $$out_ref ? 1 : 0 ;
+ }
+ return 0 if $hmm eq 0 ;
+ }
+ }
+ } ;
+}
+
+
+=item new_appender
+
+This appends a fixed string to each chunk of data read from the source
+scalar or sub. This might be useful if you're writing commands to a
+child process that always must end in a fixed string, like "\n":
+
+ run( \@cmd,
+ '<', new_appender( "\n" ), \&commands,
+ ) ;
+
+Here's a typical filter sub that might be created by new_appender():
+
+ sub newline_appender {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return input_avail && do {
+ $$out_ref = join( '', $$out_ref, $$in_ref, "\n" ) ;
+ $$in_ref = '' ;
+ 1 ;
+ }
+ } ;
+
+=cut
+
+sub new_appender($) {
+ my ( $suffix ) = @_ ;
+ croak "\$suffix undefined" unless defined $suffix ;
+
+ return sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return input_avail && do {
+ $$out_ref = join( '', $$out_ref, $$in_ref, $suffix ) ;
+ $$in_ref = '' ;
+ 1 ;
+ }
+ } ;
+}
+
+
+sub new_string_source {
+ my $ref ;
+ if ( @_ > 1 ) {
+ $ref = [ @_ ],
+ }
+ else {
+ $ref = shift ;
+ }
+
+ return ref $ref eq 'SCALAR'
+ ? sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return defined $$ref
+ ? do {
+ $$out_ref .= $$ref ;
+ my $r = length $$ref ? 1 : 0 ;
+ $$ref = undef ;
+ $r ;
+ }
+ : undef
+ }
+ : sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return @$ref
+ ? do {
+ my $s = shift @$ref ;
+ $$out_ref .= $s ;
+ length $s ? 1 : 0 ;
+ }
+ : undef ;
+ }
+}
+
+
+sub new_string_sink {
+ my ( $string_ref ) = @_ ;
+
+ return sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+
+ return input_avail && do {
+ $$string_ref .= $$in_ref ;
+ $$in_ref = '' ;
+ 1 ;
+ }
+ } ;
+}
+
+
+#=item timeout
+#
+#This function defines a time interval, starting from when start() is
+#called, or when timeout() is called. If all processes have not finished
+#by the end of the timeout period, then a "process timed out" exception
+#is thrown.
+#
+#The time interval may be passed in seconds, or as an end time in
+#"HH:MM:SS" format (any non-digit other than '.' may be used as
+#spacing and puctuation). This is probably best shown by example:
+#
+# $h->timeout( $val ) ;
+#
+# $val Effect
+# ======================== =====================================
+# undef Timeout timer disabled
+# '' Almost immediate timeout
+# 0 Almost immediate timeout
+# 0.000001 timeout > 0.0000001 seconds
+# 30 timeout > 30 seconds
+# 30.0000001 timeout > 30 seconds
+# 10:30 timeout > 10 minutes, 30 seconds
+#
+#Timeouts are currently evaluated with a 1 second resolution, though
+#this may change in the future. This means that setting
+#timeout($h,1) will cause a pokey child to be aborted sometime after
+#one second has elapsed and typically before two seconds have elapsed.
+#
+#This sub does not check whether or not the timeout has expired already.
+#
+#Returns the number of seconds set as the timeout (this does not change
+#as time passes, unless you call timeout( val ) again).
+#
+#The timeout does not include the time needed to fork() or spawn()
+#the child processes, though some setup time for the child processes can
+#included. It also does not include the length of time it takes for
+#the children to exit after they've closed all their pipes to the
+#parent process.
+#
+#=cut
+#
+#sub timeout {
+# my IPC::Run $self = shift ;
+#
+# if ( @_ ) {
+# ( $self->{TIMEOUT} ) = @_ ;
+# $self->{TIMEOUT_END} = undef ;
+# if ( defined $self->{TIMEOUT} ) {
+# if ( $self->{TIMEOUT} =~ /[^\d.]/ ) {
+# my @f = split( /[^\d\.]+/i, $self->{TIMEOUT} ) ;
+# unshift @f, 0 while @f < 3 ;
+# $self->{TIMEOUT} = (($f[0]*60)+$f[1])*60+$f[2] ;
+# }
+# elsif ( $self->{TIMEOUT} =~ /^(\d*)(?:\.(\d*))/ ) {
+# $self->{TIMEOUT} = $1 + 1 ;
+# }
+# $self->_calc_timeout_end if $self->{STATE} >= _started ;
+# }
+# }
+# return $self->{TIMEOUT} ;
+#}
+#
+#
+#sub _calc_timeout_end {
+# my IPC::Run $self = shift ;
+#
+# $self->{TIMEOUT_END} = defined $self->{TIMEOUT}
+# ? time + $self->{TIMEOUT}
+# : undef ;
+#
+# ## We add a second because we might be at the very end of the current
+# ## second, and we want to guarantee that we don't have a timeout even
+# ## one second less then the timeout period.
+# ++$self->{TIMEOUT_END} if $self->{TIMEOUT} ;
+#}
+
+=item io
+
+Takes a filename or filehandle, a redirection operator, optional filters,
+and a source or destination (depends on the redirection operator). Returns
+an IPC::Run::IO object suitable for harness()ing (including via start()
+or run()).
+
+This is shorthand for
+
+
+ require IPC::Run::IO ;
+
+ ... IPC::Run::IO->new(...) ...
+
+=cut
+
+sub io {
+ require IPC::Run::IO ;
+ IPC::Run::IO->new( @_ ) ;
+}
+
+=item timer
+
+ $h = start( \@cmd, \$in, \$out, $t = timer( 5 ) ) ;
+
+ pump $h until $out =~ /expected stuff/ || $t->is_expired ;
+
+Instantiates a non-fatal timer. pump() returns once each time a timer
+expires. Has no direct effect on run(), but you can pass a subroutine
+to fire when the timer expires.
+
+See L</timeout> for building timers that throw exceptions on
+expiration.
+
+See L<IPC::Run::Timer/timer> for details.
+
+=cut
+
+# Doing the prototype suppresses 'only used once' on older perls.
+sub timer ;
+*timer = \&IPC::Run::Timer::timer ;
+
+
+=item timeout
+
+ $h = start( \@cmd, \$in, \$out, $t = timeout( 5 ) ) ;
+
+ pump $h until $out =~ /expected stuff/ ;
+
+Instantiates a timer that throws an exception when it expires.
+If you don't provide an exception, a default exception that matches
+/^IPC::Run: .*timed out/ is thrown by default. You can pass in your own
+exception scalar or reference:
+
+ $h = start(
+ \@cmd, \$in, \$out,
+ $t = timeout( 5, exception => 'slowpoke' ),
+ ) ;
+
+or set the name used in debugging message and in the default exception
+string:
+
+ $h = start(
+ \@cmd, \$in, \$out,
+ timeout( 50, name => 'process timer' ),
+ $stall_timer = timeout( 5, name => 'stall timer' ),
+ ) ;
+
+ pump $h until $out =~ /started/ ;
+
+ $in = 'command 1' ;
+ $stall_timer->start ;
+ pump $h until $out =~ /command 1 finished/ ;
+
+ $in = 'command 2' ;
+ $stall_timer->start ;
+ pump $h until $out =~ /command 2 finished/ ;
+
+ $in = 'very slow command 3' ;
+ $stall_timer->start( 10 ) ;
+ pump $h until $out =~ /command 3 finished/ ;
+
+ $stall_timer->start( 5 ) ;
+ $in = 'command 4' ;
+ pump $h until $out =~ /command 4 finished/ ;
+
+ $stall_timer->reset; # Prevent restarting or expirng
+ finish $h ;
+
+See L</timer> for building non-fatal timers.
+
+See L<IPC::Run::Timer/timer> for details.
+
+=cut
+
+# Doing the prototype suppresses 'only used once' on older perls.
+sub timeout ;
+*timeout = \&IPC::Run::Timer::timeout ;
+
+
+=back
+
+=head1 FILTER IMPLEMENTATION FUNCTIONS
+
+These functions are for use from within filters.
+
+=over
+
+=item input_avail
+
+Returns TRUE if input is available. If none is available, then
+&get_more_input is called and its result is returned.
+
+This is usually used in preference to &get_more_input so that the
+calling filter removes all data from the $in_ref before more data
+gets read in to $in_ref.
+
+C<input_avail> is usually used as part of a return expression:
+
+ return input_avail && do {
+ ## process the input just gotten
+ 1 ;
+ } ;
+
+This technique allows input_avail to return the undef or 0 that a
+filter normally returns when there's no input to process. If a filter
+stores intermediate values, however, it will need to react to an
+undef:
+
+ my $got = input_avail ;
+ if ( ! defined $got ) {
+ ## No more input ever, flush internal buffers to $out_ref
+ }
+ return $got unless $got ;
+ ## Got some input, move as much as need be
+ return 1 if $added_to_out_ref ;
+
+=cut
+
+sub input_avail() {
+ confess "Undefined FBUF ref for $filter_num+1"
+ unless defined $filter_op->{FBUFS}->[$filter_num+1] ;
+ length ${$filter_op->{FBUFS}->[$filter_num+1]} || get_more_input ;
+}
+
+
+=item get_more_input
+
+This is used to fetch more input in to the input variable. It returns
+undef if there will never be any more input, 0 if there is none now,
+but there might be in the future, and TRUE if more input was gotten.
+
+C<get_more_input> is usually used as part of a return expression,
+see L</input_avail> for more information.
+
+=cut
+
+##
+## Filter implementation interface
+##
+sub get_more_input() {
+ ++$filter_num ;
+ my $r = eval {
+ confess "get_more_input() called and no more filters in chain"
+ unless defined $filter_op->{FILTERS}->[$filter_num] ;
+ $filter_op->{FILTERS}->[$filter_num]->(
+ $filter_op->{FBUFS}->[$filter_num+1],
+ $filter_op->{FBUFS}->[$filter_num],
+ ) ; # if defined ${$filter_op->{FBUFS}->[$filter_num+1]} ;
+ } ;
+ --$filter_num ;
+ die $@ if $@ ;
+ return $r ;
+}
+
+
+## This is not needed by most users. Should really move to IPC::Run::TestUtils
+#=item filter_tests
+#
+# my @tests = filter_tests( "foo", "in", "out", \&filter ) ;
+# $_->() for ( @tests ) ;
+#
+#This creates a list of test subs that can be used to test most filters
+#for basic functionality. The first parameter is the name of the
+#filter to be tested, the second is sample input, the third is the
+#test(s) to apply to the output(s), and the rest of the parameters are
+#the filters to be linked and tested.
+#
+#If the filter chain is to be fed multiple inputs in sequence, the second
+#parameter should be a reference to an array of thos inputs:
+#
+# my @tests = filter_tests( "foo", [qw(1 2 3)], "123", \&filter ) ;
+#
+#If the filter chain should produce a sequence of outputs, then the
+#thrid parameter should be a reference to an array of those outputs:
+#
+# my @tests = filter_tests(
+# "foo",
+# "1\n\2\n",
+# [ qr/^1$/, qr/^2$/ ],
+# new_chunker
+# ) ;
+#
+#See t/run.t and t/filter.t for an example of this in practice.
+#
+#=cut
+
+##
+## Filter testing routines
+##
+sub filter_tests($;@) {
+ my ( $name, $in, $exp, @filters ) = @_ ;
+
+ my @in = ref $in eq 'ARRAY' ? @$in : ( $in ) ;
+ my @exp = ref $exp eq 'ARRAY' ? @$exp : ( $exp ) ;
+
+ require Test ;
+ *ok = \&Test::ok ;
+
+ my IPC::Run::IO $op ;
+ my $output ;
+ my @input ;
+ my $in_count = 0 ;
+
+ my @out ;
+
+ my $h ;
+
+ return (
+ sub {
+ $h = harness() ;
+ $op = IPC::Run::IO->_new_internal( '<', 0, 0, 0, undef,
+ new_string_sink( \$output ),
+ @filters,
+ new_string_source( \@input ),
+ ) ;
+ $op->_init_filters ;
+ @input = () ;
+ $output = '' ;
+ ok(
+ ! defined $op->_do_filters( $h ),
+ 1,
+ "$name didn't pass undef (EOF) through"
+ ) ;
+ },
+
+ ## See if correctly does nothing on 0, (please try again)
+ sub {
+ $op->_init_filters ;
+ $output = '' ;
+ @input = ( '' ) ;
+ ok(
+ $op->_do_filters( $h ),
+ 0,
+ "$name didn't return 0 (please try again) when given a 0"
+ ) ;
+ },
+
+ sub {
+ @input = ( '' ) ;
+ ok(
+ $op->_do_filters( $h ),
+ 0,
+ "$name didn't return 0 (please try again) when given a second 0"
+ ) ;
+ },
+
+ sub {
+ for (1..100) {
+ last unless defined $op->_do_filters( $h ) ;
+ }
+ ok(
+ ! defined $op->_do_filters( $h ),
+ 1,
+ "$name didn't return undef (EOF) after two 0s and an undef"
+ ) ;
+ },
+
+ ## See if it can take @in and make @out
+ sub {
+ $op->_init_filters ;
+ $output = '' ;
+ @input = @in ;
+ while ( defined $op->_do_filters( $h ) && @input ) {
+ if ( length $output ) {
+ push @out, $output ;
+ $output = '' ;
+ }
+ }
+ if ( length $output ) {
+ push @out, $output ;
+ $output = '' ;
+ }
+ ok(
+ scalar @input,
+ 0,
+ "$name didn't consume it's input"
+ ) ;
+ },
+
+ sub {
+ for (1..100) {
+ last unless defined $op->_do_filters( $h ) ;
+ if ( length $output ) {
+ push @out, $output ;
+ $output = '' ;
+ }
+ }
+ ok(
+ ! defined $op->_do_filters( $h ),
+ 1,
+ "$name didn't return undef (EOF), tried 100 times"
+ ) ;
+ },
+
+ sub {
+ ok(
+ join( ', ', map "'$_'", @out ),
+ join( ', ', map "'$_'", @exp ),
+ $name
+ )
+ },
+
+ sub {
+ ## Force the harness to be cleaned up.
+ $h = undef ;
+ ok( 1 ) ;
+ }
+ ) ;
+}
+
+
+=back
+
+=head1 TODO
+
+These will be addressed as needed and as time allows.
+
+Stall timeout.
+
+Expose a list of child process objects. When I do this,
+each child process is likely to be blessed into IPC::Run::Proc.
+
+$kid->abort(), $kid->kill(), $kid->signal( $num_or_name ).
+
+Write tests for /(full_)?results?/ subs.
+
+Currently, pump() and run() only work on systems where select() works on the
+filehandles returned by pipe(). This does *not* include ActiveState on Win32,
+although it does work on cygwin under Win32 (thought the tests whine a bit).
+I'd like to rectify that, suggestions and patches welcome.
+
+Likewise start() only fully works on fork()/exec() machines (well, just
+fork() if you only ever pass perl subs as subprocesses). There's
+some scaffolding for calling Open3::spawn_with_handles(), but that's
+untested, and not that useful with limited select().
+
+Support for C<\@sub_cmd> as an argument to a command which
+gets replaced with /dev/fd or the name of a temporary file containing foo's
+output. This is like <(sub_cmd ...) found in bash and csh (IIRC).
+
+Allow multiple harnesses to be combined as independant sets of processes
+in to one 'meta-harness'.
+
+Allow a harness to be passed in place of an \@cmd. This would allow
+multiple harnesses to be aggregated.
+
+Ability to add external file descriptors w/ filter chains and endpoints.
+
+Ability to add timeouts and timing generators (i.e. repeating timeouts).
+
+High resolution timeouts.
+
+=head1 Win32 LIMITATIONS
+
+=over
+
+=item Fails on Win9X
+
+If you want Win9X support, you'll have to debug it or fund me because I
+don't use that system any more. The Win32 subsysem has been extended to
+use temporary files in simple run() invocations and these may actually
+work on Win9X too, but I don't have time to work on it.
+
+=item May deadlock on Win2K (but not WinNT4 or WinXPPro)
+
+Spawning more than one subprocess on Win2K causes a deadlock I haven't
+figured out yet, but simple uses of run() often work. Passes all tests
+on WinXPPro and WinNT.
+
+=item no support yet for <pty< and >pty>
+
+These are likely to be implemented as "<" and ">" with binmode on, not
+sure.
+
+=item no support for file descriptors higher than 2 (stderr)
+
+Win32 only allows passing explicit fds 0, 1, and 2. If you really, really need to pass file handles, us Win32API:: GetOsFHandle() or ::FdGetOsFHandle() to
+get the integer handle and pass it to the child process using the command
+line, environment, stdin, intermediary file, or other IPC mechnism. Then
+use that handle in the child (Win32API.pm provides ways to reconstitute
+Perl file handles from Win32 file handles).
+
+=item no support for subroutine subprocesses (CODE refs)
+
+Can't fork(), so the subroutines would have no context, and closures certainly
+have no meaning
+
+Perhaps with Win32 fork() emulation, this can be supported in a limited
+fashion, but there are other very serious problems with that: all parent
+fds get dup()ed in to the thread emulating the forked process, and that
+keeps the parent from being able to close all of the appropriate fds.
+
+=item no support for init => sub {} routines.
+
+Win32 processes are created from scratch, there is no way to do an init
+routine that will affect the running child. Some limited support might
+be implemented one day, do chdir() and %ENV changes can be made.
+
+=item signals
+
+Win32 does not fully support signals. signal() is likely to cause errors
+unless sending a signal that Perl emulates, and C<kill_kill()> is immediately
+fatal (there is no grace period).
+
+=item helper processes
+
+IPC::Run uses helper processes, one per redirected file, to adapt between the
+anonymous pipe connected to the child and the TCP socket connected to the
+parent. This is a waste of resources and will change in the future to either
+use threads (instead of helper processes) or a WaitForMultipleObjects call
+(instead of select). Please contact me if you can help with the
+WaitForMultipleObjects() approach; I haven't figured out how to get at it
+without C code.
+
+=item shutdown pause
+
+There seems to be a pause of up to 1 second between when a child program exits
+and the corresponding sockets indicate that they are closed in the parent.
+Not sure why.
+
+=item binmode
+
+binmode is not supported yet. The underpinnings are implemented, just ask
+if you need it.
+
+=item IPC::Run::IO
+
+IPC::Run::IO objects can be used on Unix to read or write arbitrary files. On
+Win32, they will need to use the same helper processes to adapt from
+non-select()able filehandles to select()able ones (or perhaps
+WaitForMultipleObjects() will work with them, not sure).
+
+=item startup race conditions
+
+There seems to be an occasional race condition between child process startup
+and pipe closings. It seems like if the child is not fully created by the time
+CreateProcess returns and we close the TCP socket being handed to it, the
+parent socket can also get closed. This is seen with the Win32 pumper
+applications, not the "real" child process being spawned.
+
+I assume this is because the kernel hasn't gotten around to incrementing the
+reference count on the child's end (since the child was slow in starting), so
+the parent's closing of the child end causes the socket to be closed, thus
+closing the parent socket.
+
+Being a race condition, it's hard to reproduce, but I encountered it while
+testing this code on a drive share to a samba box. In this case, it takes
+t/run.t a long time to spawn it's chile processes (the parent hangs in the
+first select for several seconds until the child emits any debugging output).
+
+I have not seen it on local drives, and can't reproduce it at will,
+unfortunately. The symptom is a "bad file descriptor in select()" error, and,
+by turning on debugging, it's possible to see that select() is being called on
+a no longer open file descriptor that was returned from the _socket() routine
+in Win32Helper. There's a new confess() that checks for this ("PARENT_HANDLE
+no longer open"), but I haven't been able to reproduce it (typically).
+
+=back
+
+=head1 LIMITATIONS
+
+On Unix, requires a system that supports C<waitpid( $pid, WNOHANG )> so
+it can tell if a child process is still running.
+
+PTYs don't seem to be non-blocking on some versions of Solaris. Here's a
+test script contributed by Borislav Deianov <borislav@ensim.com> to see
+if you have the problem. If it dies, you have the problem.
+
+ #!/usr/bin/perl
+
+ use IPC::Run qw(run);
+ use Fcntl;
+ use IO::Pty;
+
+ sub makecmd {
+ return ['perl', '-e',
+ '<STDIN>, print "\n" x '.$_[0].'; while(<STDIN>){last if /end/}'];
+ }
+
+ #pipe R, W;
+ #fcntl(W, F_SETFL, O_NONBLOCK);
+ #while (syswrite(W, "\n", 1)) { $pipebuf++ };
+ #print "pipe buffer size is $pipebuf\n";
+ my $pipebuf=4096;
+ my $in = "\n" x ($pipebuf * 2) . "end\n";
+ my $out;
+
+ $SIG{ALRM} = sub { die "Never completed!\n" } ;
+
+ print "reading from scalar via pipe...";
+ alarm( 2 ) ;
+ run(makecmd($pipebuf * 2), '<', \$in, '>', \$out);
+ alarm( 0 );
+ print "done\n";
+
+ print "reading from code via pipe... ";
+ alarm( 2 ) ;
+ run(makecmd($pipebuf * 3), '<', sub { $t = $in; undef $in; $t}, '>', \$out);
+ alarm( 0 ) ;
+ print "done\n";
+
+ $pty = IO::Pty->new();
+ $pty->blocking(0);
+ $slave = $pty->slave();
+ while ($pty->syswrite("\n", 1)) { $ptybuf++ };
+ print "pty buffer size is $ptybuf\n";
+ $in = "\n" x ($ptybuf * 3) . "end\n";
+
+ print "reading via pty... ";
+ alarm( 2 ) ;
+ run(makecmd($ptybuf * 3), '<pty<', \$in, '>', \$out);
+ alarm(0);
+ print "done\n";
+
+No support for ';', '&&', '||', '{ ... }', etc: use perl's, since run()
+returns TRUE when the command exits with a 0 result code.
+
+Does not provide shell-like string interpolation.
+
+No support for C<cd>, C<setenv>, or C<export>: do these in an init() sub
+
+ run(
+ \cmd,
+ ...
+ init => sub {
+ chdir $dir or die $! ;
+ $ENV{FOO}='BAR'
+ }
+ ) ;
+
+Timeout calculation does not allow absolute times, or specification of
+days, months, etc.
+
+B<WARNING:> Function coprocesses (C<run \&foo, ...>) suffer from two
+limitations. The first is that it is difficult to close all filehandles the
+child inherits from the parent, since there is no way to scan all open
+FILEHANDLEs in Perl and it both painful and a bit dangerous to close all open
+file descriptors with C<POSIX::close()>. Painful because we can't tell which
+fds are open at the POSIX level, either, so we'd have to scan all possible fds
+and close any that we don't want open (normally C<exec()> closes any
+non-inheritable but we don't C<exec()> for &sub processes.
+
+The second problem is that Perl's DESTROY subs and other on-exit cleanup gets
+run in the child process. If objects are instantiated in the parent before the
+child is forked, the the DESTROY will get run once in the parent and once in
+the child. When coprocess subs exit, POSIX::exit is called to work around this,
+but it means that objects that are still referred to at that time are not
+cleaned up. So setting package vars or closure vars to point to objects that
+rely on DESTROY to affect things outside the process (files, etc), will
+lead to bugs.
+
+I goofed on the syntax: "<pipe" vs. "<pty<" and ">filename" are both
+oddities.
+
+=head1 TODO
+
+=over
+
+=item Allow one harness to "adopt" another:
+
+ $new_h = harness \@cmd2 ;
+ $h->adopt( $new_h ) ;
+
+=item Close all filehandles not explicitly marked to stay open.
+
+The problem with this one is that there's no good way to scan all open
+FILEHANDLEs in Perl, yet you don't want child processes inheriting handles
+willy-nilly.
+
+=back
+
+=head1 INSPIRATION
+
+Well, select() and waitpid() badly needed wrapping, and open3() isn't
+open-minded enough for me.
+
+The shell-like API inspired by a message Russ Allbery sent to perl5-porters,
+which included:
+
+ I've thought for some time that it would be
+ nice to have a module that could handle full Bourne shell pipe syntax
+ internally, with fork and exec, without ever invoking a shell. Something
+ that you could give things like:
+
+ pipeopen (PIPE, [ qw/cat file/ ], '|', [ 'analyze', @args ], '>&3');
+
+Message ylln51p2b6.fsf@windlord.stanford.edu, on 2000/02/04.
+
+=head1 AUTHOR
+
+Barrie Slaymaker <barries@slaysys.com>, with numerous suggestions by p5p.
+
+=cut
+
+1 ;
--- /dev/null
+package IPC::Run::Debug;
+
+=head1 NAME
+
+IPC::Run::Debug - debugging routines for IPC::Run
+
+=head1 SYNOPSIS
+
+ ##
+ ## Environment variable usage
+ ##
+ ## To force debugging off and shave a bit of CPU and memory
+ ## by compile-time optimizing away all debugging code in IPC::Run
+ ## (debug => ...) options to IPC::Run will be ignored.
+ export IPCRUNDEBUG=none
+
+ ## To force debugging on (levels are from 0..10)
+ export IPCRUNDEBUG=basic
+
+ ## Leave unset or set to "" to compile in debugging support and
+ ## allow runtime control of it using the debug option.
+
+=head1 DESCRIPTION
+
+Controls IPC::Run debugging. Debugging levels are now set by using words,
+but the numbers shown are still supported for backwards compatability:
+
+ 0 none disabled (special, see below)
+ 1 basic what's running
+ 2 data what's being sent/recieved
+ 3 details what's going on in more detail
+ 4 gory way too much detail for most uses
+ 10 all use this when submitting bug reports
+ noopts optimizations forbidden due to inherited STDIN
+
+The C<none> level is special when the environment variable IPCRUNDEBUG
+is set to this the first time IPC::Run::Debug is loaded: it prevents
+the debugging code from being compiled in to the remaining IPC::Run modules,
+saving a bit of cpu.
+
+To do this in a script, here's a way that allows it to be overridden:
+
+ BEGIN {
+ unless ( defined $ENV{IPCRUNDEBUG} ) {
+ eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
+ or die $@;
+ }
+ }
+
+This should force IPC::Run to not be debuggable unless somebody sets
+the IPCRUNDEBUG flag; modify this formula to grep @ARGV if need be:
+
+ BEGIN {
+ unless ( grep /^--debug/, @ARGV ) {
+ eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"'
+ or die $@;
+ }
+
+Both of those are untested.
+
+=cut
+
+@ISA = qw( Exporter ) ;
+
+## We use @EXPORT for the end user's convenience: there's only one function
+## exported, it's homonymous with the module, it's an unusual name, and
+## it can be suppressed by "use IPC::Run () ;".
+
+@EXPORT = qw(
+ _debug
+ _debug_desc_fd
+ _debugging
+ _debugging_data
+ _debugging_details
+ _debugging_gory_details
+ _debugging_not_optimized
+ _set_child_debug_name
+);
+
+
+@EXPORT_OK = qw(
+ _debug_init
+ _debugging_level
+ _map_fds
+);
+
+%EXPORT_TAGS = (
+ default => \@EXPORT,
+ all => [ @EXPORT, @EXPORT_OK ],
+);
+
+use strict ;
+use Exporter ;
+
+my $disable_debugging =
+ defined $ENV{IPCRUNDEBUG}
+ && (
+ ! $ENV{IPCRUNDEBUG}
+ || lc $ENV{IPCRUNDEBUG} eq "none"
+ );
+
+eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@;
+sub _map_fds() { "" }
+sub _debug {}
+sub _debug_desc_fd {}
+sub _debug_init {}
+sub _set_child_debug_name {}
+sub _debugging() { 0 }
+sub _debugging_level() { 0 }
+sub _debugging_data() { 0 }
+sub _debugging_details() { 0 }
+sub _debugging_gory_details() { 0 }
+sub _debugging_not_optimized() { 0 }
+
+1;
+STUBS
+
+use POSIX;
+use UNIVERSAL qw( isa );
+
+sub _map_fds {
+ my $map = '' ;
+ my $digit = 0 ;
+ my $in_use ;
+ my $dummy ;
+ for my $fd (0..63) {
+ ## I'd like a quicker way (less user, cpu & expecially sys and kernal
+ ## calls) to detect open file descriptors. Let me know...
+ ## Hmmm, could do a 0 length read and check for bad file descriptor...
+ ## but that segfaults on Win32
+ my $test_fd = POSIX::dup( $fd ) ;
+ $in_use = defined $test_fd ;
+ POSIX::close $test_fd if $in_use ;
+ $map .= $in_use ? $digit : '-';
+ $digit = 0 if ++$digit > 9 ;
+ }
+ warn "No fds open???" unless $map =~ /\d/ ;
+ $map =~ s/(.{1,12})-*$/$1/ ;
+ return $map ;
+}
+
+use vars qw( $parent_pid ) ;
+
+$parent_pid = $$ ;
+
+## TODO: move debugging to it's own module and make it compile-time
+## optimizable.
+
+## Give kid process debugging nice names
+my $debug_name ;
+
+sub _set_child_debug_name {
+ $debug_name = shift;
+}
+
+## There's a bit of hackery going on here.
+##
+## We want to have any code anywhere be able to emit
+## debugging statements without knowing what harness the code is
+## being called in/from, since we'd need to pass a harness around to
+## everything.
+##
+## Thus, $cur_self was born.
+#
+my %debug_levels = (
+ none => 0,
+ basic => 1,
+ data => 2,
+ details => 3,
+ gore => 4,
+ gory_details => 4,
+ "gory details" => 4,
+ gory => 4,
+ gorydetails => 4,
+ all => 10,
+ notopt => 0,
+);
+
+my $warned;
+
+sub _debugging_level() {
+ my $level = 0 ;
+
+ $level = $IPC::Run::cur_self->{debug} || 0
+ if $IPC::Run::cur_self
+ && ( $IPC::Run::cur_self->{debug} || 0 ) >= $level ;
+
+ if ( defined $ENV{IPCRUNDEBUG} ) {
+ my $v = $ENV{IPCRUNDEBUG};
+ $v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/;
+ unless ( defined $v ) {
+ $warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n";
+ $v = 1;
+ }
+ $level = $v if $v > $level ;
+ }
+ return $level ;
+}
+
+sub _debugging_atleast($) {
+ my $min_level = shift || 1 ;
+
+ my $level = _debugging_level ;
+
+ return $level >= $min_level ? $level : 0 ;
+}
+
+sub _debugging() { _debugging_atleast 1 }
+sub _debugging_data() { _debugging_atleast 2 }
+sub _debugging_details() { _debugging_atleast 3 }
+sub _debugging_gory_details() { _debugging_atleast 4 }
+sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" }
+
+sub _debug_init {
+ ## This routine is called only in spawned children to fake out the
+ ## debug routines so they'll emit debugging info.
+ $IPC::Run::cur_self = {} ;
+ ( $parent_pid,
+ $^T,
+ $IPC::Run::cur_self->{debug},
+ $IPC::Run::cur_self->{DEBUG_FD},
+ $debug_name
+ ) = @_ ;
+}
+
+
+sub _debug {
+# return unless _debugging || _debugging_not_optimized ;
+
+ my $fd = defined &IPC::Run::_debug_fd
+ ? IPC::Run::_debug_fd()
+ : fileno STDERR;
+
+ my $s ;
+ my $debug_id ;
+ $debug_id = join(
+ " ",
+ join(
+ "",
+ defined $IPC::Run::cur_self ? "#$IPC::Run::cur_self->{ID}" : (),
+ "($$)",
+ ),
+ defined $debug_name && length $debug_name ? $debug_name : (),
+ ) ;
+ my $prefix = join(
+ "",
+ "IPC::Run",
+ sprintf( " %04d", time - $^T ),
+ ( _debugging_details ? ( " ", _map_fds ) : () ),
+ length $debug_id ? ( " [", $debug_id, "]" ) : (),
+ ": ",
+ ) ;
+
+ my $msg = join( '', map defined $_ ? $_ : "<undef>", @_ ) ;
+ chomp $msg ;
+ $msg =~ s{^}{$prefix}gm ;
+ $msg .= "\n" ;
+ POSIX::write( $fd, $msg, length $msg ) ;
+}
+
+
+my @fd_descs = ( 'stdin', 'stdout', 'stderr' ) ;
+
+sub _debug_desc_fd {
+ return unless _debugging ;
+ my $text = shift ;
+ my $op = pop ;
+ my $kid = $_[0] ;
+
+Carp::carp join " ", caller(0), $text, $op if defined $op && isa( $op, "IO::Pty" ) ;
+
+ _debug(
+ $text,
+ ' ',
+ ( defined $op->{FD}
+ ? $op->{FD} < 3
+ ? ( $fd_descs[$op->{FD}] )
+ : ( 'fd ', $op->{FD} )
+ : $op->{FD}
+ ),
+ ( defined $op->{KFD}
+ ? (
+ ' (kid',
+ ( defined $kid ? ( ' ', $kid->{NUM}, ) : () ),
+ "'s ",
+ ( $op->{KFD} < 3
+ ? $fd_descs[$op->{KFD}]
+ : defined $kid
+ && defined $kid->{DEBUG_FD}
+ && $op->{KFD} == $kid->{DEBUG_FD}
+ ? ( 'debug (', $op->{KFD}, ')' )
+ : ( 'fd ', $op->{KFD} )
+ ),
+ ')',
+ )
+ : ()
+ ),
+ ) ;
+}
+
+1;
+
+SUBS
+
+=head1 AUTHOR
+
+Barrie Slaymaker <barries@slaysys.com>, with numerous suggestions by p5p.
+
+=cut
+
+1 ;
--- /dev/null
+package IPC::Run::IO ;
+
+=head1 NAME
+
+IPC::Run::IO -- I/O channels for IPC::Run.
+
+=head1 SYNOPSIS
+
+B<NOT IMPLEMENTED YET ON Win32! Win32 does not allow select() on
+normal file descriptors; IPC::RUN::IO needs to use IPC::Run::Win32Helper
+to do this.>
+
+ use IPC::Run qw( io ) ;
+
+ ## The sense of '>' and '<' is opposite of perl's open(),
+ ## but agrees with IPC::Run.
+ $io = io( "filename", '>', \$recv ) ;
+ $io = io( "filename", 'r', \$recv ) ;
+
+ ## Append to $recv:
+ $io = io( "filename", '>>', \$recv ) ;
+ $io = io( "filename", 'ra', \$recv ) ;
+
+ $io = io( "filename", '<', \$send ) ;
+ $io = io( "filename", 'w', \$send ) ;
+
+ $io = io( "filename", '<<', \$send ) ;
+ $io = io( "filename", 'wa', \$send ) ;
+
+ ## Handles / IO objects that the caller opens:
+ $io = io( \*HANDLE, '<', \$send ) ;
+
+ $f = IO::Handle->new( ... ) ; # Any subclass of IO::Handle
+ $io = io( $f, '<', \$send ) ;
+
+ require IPC::Run::IO ;
+ $io = IPC::Run::IO->new( ... ) ;
+
+ ## Then run(), harness(), or start():
+ run $io, ... ;
+
+ ## You can, of course, use io() or IPC::Run::IO->new() as an
+ ## argument to run(), harness, or start():
+ run io( ... ) ;
+
+
+=head1 DESCRIPTION
+
+This class and module allows filehandles and filenames to be harnessed for
+I/O when used IPC::Run, independant of anything else IPC::Run is doing
+(except that errors & exceptions can affect all things that IPC::Run is
+doing).
+
+=head1 SUBCLASSING
+
+INCOMPATIBLE CHANGE: due to the awkwardness introduced in ripping pseudohashes
+out of Perl, this class I<no longer> uses the fields pragma.
+
+=head1 TODO
+
+Implement bidirectionality.
+
+=head1 AUTHOR
+
+Barrie Slaymaker <barries@slaysys.com>
+
+=cut ;
+
+## This class is also used internally by IPC::Run in a very initimate way,
+## since this is a partial factoring of code from IPC::Run plus some code
+## needed to do standalone channels. This factoring process will continue
+## at some point. Don't know how far how fast.
+
+use strict ;
+use Carp ;
+use Fcntl ;
+use Symbol ;
+use UNIVERSAL qw( isa ) ;
+
+use IPC::Run::Debug;
+use IPC::Run qw( Win32_MODE );
+
+BEGIN {
+ if ( Win32_MODE ) {
+ eval "use IPC::Run::Win32Helper; require IPC::Run::Win32IO; 1"
+ or ( $@ && die ) or die "$!" ;
+ }
+}
+
+sub _empty($) ;
+
+*_empty = \&IPC::Run::_empty ;
+
+
+sub new {
+ my $class = shift ;
+ $class = ref $class || $class ;
+
+ my ( $external, $type, $internal ) = ( shift, shift, pop ) ;
+
+ croak "$class: '$_' is not a valid I/O operator"
+ unless $type =~ /^(?:<<?|>>?)$/ ;
+
+ my IPC::Run::IO $self = $class->_new_internal(
+ $type, undef, undef, $internal, undef, @_
+ ) ;
+
+ if ( ! ref $external ) {
+ $self->{FILENAME} = $external ;
+ }
+ elsif ( ref eq 'GLOB' || isa( $external, 'IO::Handle' ) ) {
+ $self->{HANDLE} = $external ;
+ $self->{DONT_CLOSE} = 1 ;
+ }
+ else {
+ croak "$class: cannot accept " . ref( $external ) . " to do I/O with" ;
+ }
+
+ return $self ;
+}
+
+
+## IPC::Run uses this ctor, since it preparses things and needs more
+## smarts.
+sub _new_internal {
+ my $class = shift ;
+ $class = ref $class || $class ;
+
+ $class = "IPC::Run::Win32IO"
+ if Win32_MODE && $class eq "IPC::Run::IO";
+
+ my IPC::Run::IO $self ;
+ $self = bless {}, $class ;
+
+ my ( $type, $kfd, $pty_id, $internal, $binmode, @filters ) = @_ ;
+
+ # Older perls (<=5.00503, at least) don't do list assign to
+ # psuedo-hashes well.
+ $self->{TYPE} = $type ;
+ $self->{KFD} = $kfd ;
+ $self->{PTY_ID} = $pty_id ;
+ $self->binmode( $binmode ) ;
+ $self->{FILTERS} = [ @filters ] ;
+
+ ## Add an adapter to the end of the filter chain (which is usually just the
+ ## read/writer sub pushed by IPC::Run) to the DEST or SOURCE, if need be.
+ if ( $self->op =~ />/ ) {
+ croak "'$_' missing a destination" if _empty $internal ;
+ $self->{DEST} = $internal ;
+ if ( isa( $self->{DEST}, 'CODE' ) ) {
+ ## Put a filter on the end of the filter chain to pass the
+ ## output on to the CODE ref. For SCALAR refs, the last
+ ## filter in the chain writes directly to the scalar itself. See
+ ## _init_filters(). For CODE refs, however, we need to adapt from
+ ## the SCALAR to calling the CODE.
+ unshift(
+ @{$self->{FILTERS}},
+ sub {
+ my ( $in_ref ) = @_ ;
+
+ return IPC::Run::input_avail() && do {
+ $self->{DEST}->( $$in_ref ) ;
+ $$in_ref = '' ;
+ 1 ;
+ }
+ }
+ ) ;
+ }
+ }
+ else {
+ croak "'$_' missing a source" if _empty $internal ;
+ $self->{SOURCE} = $internal ;
+ if ( isa( $internal, 'CODE' ) ) {
+ push(
+ @{$self->{FILTERS}},
+ sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+ return 0 if length $$out_ref ;
+
+ return undef
+ if $self->{SOURCE_EMPTY} ;
+
+ my $in = $internal->() ;
+ unless ( defined $in ) {
+ $self->{SOURCE_EMPTY} = 1 ;
+ return undef
+ }
+ return 0 unless length $in ;
+ $$out_ref = $in ;
+
+ return 1 ;
+ }
+ ) ;
+ }
+ elsif ( isa( $internal, 'SCALAR' ) ) {
+ push(
+ @{$self->{FILTERS}},
+ sub {
+ my ( $in_ref, $out_ref ) = @_ ;
+ return 0 if length $$out_ref ;
+
+ ## pump() clears auto_close_ins, finish() sets it.
+ return $self->{HARNESS}->{auto_close_ins} ? undef : 0
+ if IPC::Run::_empty ${$self->{SOURCE}}
+ || $self->{SOURCE_EMPTY} ;
+
+ $$out_ref = $$internal ;
+ eval { $$internal = '' }
+ if $self->{HARNESS}->{clear_ins} ;
+
+ $self->{SOURCE_EMPTY} = $self->{HARNESS}->{auto_close_ins} ;
+
+ return 1 ;
+ }
+ ) ;
+ }
+ }
+
+ return $self ;
+}
+
+
+=item filename
+
+Gets/sets the filename. Returns the value after the name change, if
+any.
+
+=cut
+
+sub filename {
+ my IPC::Run::IO $self = shift ;
+ $self->{FILENAME} = shift if @_ ;
+ return $self->{FILENAME} ;
+}
+
+
+=item init
+
+Does initialization required before this can be run. This includes open()ing
+the file, if necessary, and clearing the destination scalar if necessary.
+
+=cut
+
+sub init {
+ my IPC::Run::IO $self = shift ;
+
+ $self->{SOURCE_EMPTY} = 0 ;
+ ${$self->{DEST}} = ''
+ if $self->mode =~ /r/ && ref $self->{DEST} eq 'SCALAR' ;
+
+ $self->open if defined $self->filename ;
+ $self->{FD} = $self->fileno ;
+
+ if ( ! $self->{FILTERS} ) {
+ $self->{FBUFS} = undef ;
+ }
+ else {
+ @{$self->{FBUFS}} = map {
+ my $s = "" ;
+ \$s ;
+ } ( @{$self->{FILTERS}}, '' ) ;
+
+ $self->{FBUFS}->[0] = $self->{DEST}
+ if $self->{DEST} && ref $self->{DEST} eq 'SCALAR' ;
+ push @{$self->{FBUFS}}, $self->{SOURCE} ;
+ }
+
+ return undef ;
+}
+
+
+=item open
+
+If a filename was passed in, opens it. Determines if the handle is open
+via fileno(). Throws an exception on error.
+
+=cut
+
+my %open_flags = (
+ '>' => O_RDONLY,
+ '>>' => O_RDONLY,
+ '<' => O_WRONLY | O_CREAT | O_TRUNC,
+ '<<' => O_WRONLY | O_CREAT | O_APPEND,
+) ;
+
+sub open {
+ my IPC::Run::IO $self = shift ;
+
+ croak "IPC::Run::IO: Can't open() a file with no name"
+ unless defined $self->{FILENAME} ;
+ $self->{HANDLE} = gensym unless $self->{HANDLE} ;
+
+ _debug
+ "opening '", $self->filename, "' mode '", $self->mode, "'"
+ if _debugging_data ;
+ sysopen(
+ $self->{HANDLE},
+ $self->filename,
+ $open_flags{$self->op},
+ ) or croak
+ "IPC::Run::IO: $! opening '$self->{FILENAME}', mode '" . $self->mode . "'" ;
+
+ return undef ;
+}
+
+
+=item open_pipe
+
+If this is a redirection IO object, this opens the pipe in a platform
+independant manner.
+
+=cut
+
+sub _do_open {
+ my $self = shift;
+ my ( $child_debug_fd, $parent_handle ) = @_ ;
+
+
+ if ( $self->dir eq "<" ) {
+ ( $self->{TFD}, $self->{FD} ) = IPC::Run::_pipe_nb ;
+ if ( $parent_handle ) {
+ CORE::open $parent_handle, ">&=$self->{FD}"
+ or croak "$! duping write end of pipe for caller" ;
+ }
+ }
+ else {
+ ( $self->{FD}, $self->{TFD} ) = IPC::Run::_pipe ;
+ if ( $parent_handle ) {
+ CORE::open $parent_handle, "<&=$self->{FD}"
+ or croak "$! duping read end of pipe for caller" ;
+ }
+ }
+}
+
+sub open_pipe {
+ my IPC::Run::IO $self = shift ;
+
+ ## Hmmm, Maybe allow named pipes one day. But until then...
+ croak "IPC::Run::IO: Can't pipe() when a file name has been set"
+ if defined $self->{FILENAME} ;
+
+ $self->_do_open( @_ );
+
+ ## return ( child_fd, parent_fd )
+ return $self->dir eq "<"
+ ? ( $self->{TFD}, $self->{FD} )
+ : ( $self->{FD}, $self->{TFD} ) ;
+}
+
+
+sub _cleanup { ## Called from Run.pm's _cleanup
+ my $self = shift;
+ undef $self->{FAKE_PIPE};
+}
+
+
+=item close
+
+Closes the handle. Throws an exception on failure.
+
+
+=cut
+
+sub close {
+ my IPC::Run::IO $self = shift ;
+
+ if ( defined $self->{HANDLE} ) {
+ close $self->{HANDLE}
+ or croak( "IPC::Run::IO: $! closing "
+ . ( defined $self->{FILENAME}
+ ? "'$self->{FILENAME}'"
+ : "handle"
+ )
+ ) ;
+ }
+ else {
+ IPC::Run::_close( $self->{FD} ) ;
+ }
+
+ $self->{FD} = undef ;
+
+ return undef ;
+}
+
+=item fileno
+
+Returns the fileno of the handle. Throws an exception on failure.
+
+
+=cut
+
+sub fileno {
+ my IPC::Run::IO $self = shift ;
+
+ my $fd = fileno $self->{HANDLE} ;
+ croak( "IPC::Run::IO: $! "
+ . ( defined $self->{FILENAME}
+ ? "'$self->{FILENAME}'"
+ : "handle"
+ )
+ ) unless defined $fd ;
+
+ return $fd ;
+}
+
+=item mode
+
+Returns the operator in terms of 'r', 'w', and 'a'. There is a state
+'ra', unlike Perl's open(), which indicates that data read from the
+handle or file will be appended to the output if the output is a scalar.
+This is only meaningful if the output is a scalar, it has no effect if
+the output is a subroutine.
+
+The redirection operators can be a little confusing, so here's a reference
+table:
+
+ > r Read from handle in to process
+ < w Write from process out to handle
+ >> ra Read from handle in to process, appending it to existing
+ data if the destination is a scalar.
+ << wa Write from process out to handle, appending to existing
+ data if IPC::Run::IO opened a named file.
+
+=cut
+
+sub mode {
+ my IPC::Run::IO $self = shift ;
+
+ croak "IPC::Run::IO: unexpected arguments for mode(): @_" if @_ ;
+
+ ## TODO: Optimize this
+ return ( $self->{TYPE} =~ /</ ? 'w' : 'r' ) .
+ ( $self->{TYPE} =~ /<<|>>/ ? 'a' : '' ) ;
+}
+
+
+=item op
+
+Returns the operation: '<', '>', '<<', '>>'. See L</mode> if you want
+to spell these 'r', 'w', etc.
+
+=cut
+
+sub op {
+ my IPC::Run::IO $self = shift ;
+
+ croak "IPC::Run::IO: unexpected arguments for op(): @_" if @_ ;
+
+ return $self->{TYPE} ;
+}
+
+=item binmode
+
+Sets/gets whether this pipe is in binmode or not. No effect off of Win32
+OSs, of course, and on Win32, no effect after the harness is start()ed.
+
+=cut
+
+sub binmode {
+ my IPC::Run::IO $self = shift ;
+
+ $self->{BINMODE} = shift if @_ ;
+
+ return $self->{BINMODE} ;
+}
+
+
+=item dir
+
+Returns the first character of $self->op. This is either "<" or ">".
+
+=cut
+
+sub dir {
+ my IPC::Run::IO $self = shift ;
+
+ croak "IPC::Run::IO: unexpected arguments for dir(): @_" if @_ ;
+
+ return substr $self->{TYPE}, 0, 1 ;
+}
+
+
+##
+## Filter Scaffolding
+##
+#my $filter_op ; ## The op running a filter chain right now
+#my $filter_num ; ## Which filter is being run right now.
+
+use vars (
+'$filter_op', ## The op running a filter chain right now
+'$filter_num' ## Which filter is being run right now.
+) ;
+
+sub _init_filters {
+ my IPC::Run::IO $self = shift ;
+
+confess "\$self not an IPC::Run::IO" unless isa( $self, "IPC::Run::IO" ) ;
+ $self->{FBUFS} = [] ;
+
+ $self->{FBUFS}->[0] = $self->{DEST}
+ if $self->{DEST} && ref $self->{DEST} eq 'SCALAR' ;
+
+ return unless $self->{FILTERS} && @{$self->{FILTERS}} ;
+
+ push @{$self->{FBUFS}}, map {
+ my $s = "" ;
+ \$s ;
+ } ( @{$self->{FILTERS}}, '' ) ;
+
+ push @{$self->{FBUFS}}, $self->{SOURCE} ;
+}
+
+
+sub poll {
+ my IPC::Run::IO $self = shift;
+ my ( $harness ) = @_;
+
+ if ( defined $self->{FD} ) {
+ my $d = $self->dir;
+ if ( $d eq "<" ) {
+ if ( vec $harness->{WOUT}, $self->{FD}, 1 ) {
+ _debug_desc_fd( "filtering data to", $self )
+ if _debugging_details ;
+ return $self->_do_filters( $harness );
+ }
+ }
+ elsif ( $d eq ">" ) {
+ if ( vec $harness->{ROUT}, $self->{FD}, 1 ) {
+ _debug_desc_fd( "filtering data from", $self )
+ if _debugging_details ;
+ return $self->_do_filters( $harness );
+ }
+ }
+ }
+ return 0;
+}
+
+
+sub _do_filters {
+ my IPC::Run::IO $self = shift ;
+
+ ( $self->{HARNESS} ) = @_ ;
+
+ my ( $saved_op, $saved_num ) =($IPC::Run::filter_op,$IPC::Run::filter_num) ;
+ $IPC::Run::filter_op = $self ;
+ $IPC::Run::filter_num = -1 ;
+ my $c = 0;
+ my $r;
+ {
+ $@ = '';
+ $r = eval { IPC::Run::get_more_input() ; } ;
+ $c++;
+ ##$@ and warn "redo ", substr($@, 0, 20) , " ";
+ (($c < 200) and ($@||'')=~ m/^Resource temporarily/) and redo;
+ }
+ ( $IPC::Run::filter_op, $IPC::Run::filter_num ) = ( $saved_op, $saved_num ) ;
+ $self->{HARNESS} = undef ;
+ die "ack ", $@ if $@ ;
+ return $r ;
+}
+
+1 ;
--- /dev/null
+package IPC::Run::Timer ;
+
+=head1 NAME
+
+IPC::Run::Timer -- Timer channels for IPC::Run.
+
+=head1 SYNOPSIS
+
+ use IPC::Run qw( run timer timeout ) ;
+ ## or IPC::Run::Timer ( timer timeout ) ;
+ ## or IPC::Run::Timer ( :all ) ;
+
+ ## A non-fatal timer:
+ $t = timer( 5 ) ; # or...
+ $t = IO::Run::Timer->new( 5 ) ;
+ run $t, ... ;
+
+ ## A timeout (which is a timer that dies on expiry):
+ $t = timeout( 5 ) ; # or...
+ $t = IO::Run::Timer->new( 5, exception => "harness timed out" ) ;
+
+=head1 DESCRIPTION
+
+This class and module allows timers and timeouts to be created for use
+by IPC::Run. A timer simply expires when it's time is up. A timeout
+is a timer that throws an exception when it expires.
+
+Timeouts are usually a bit simpler to use than timers: they throw an
+exception on expiration so you don't need to check them:
+
+ ## Give @cmd 10 seconds to get started, then 5 seconds to respond
+ my $t = timeout( 10 ) ;
+ $h = start(
+ \@cmd, \$in, \$out,
+ $t,
+ ) ;
+ pump $h until $out =~ /prompt/ ;
+
+ $in = "some stimulus" ;
+ $out = '' ;
+ $t->time( 5 )
+ pump $h until $out =~ /expected response/ ;
+
+You do need to check timers:
+
+ ## Give @cmd 10 seconds to get started, then 5 seconds to respond
+ my $t = timer( 10 ) ;
+ $h = start(
+ \@cmd, \$in, \$out,
+ $t,
+ ) ;
+ pump $h until $t->is_expired || $out =~ /prompt/ ;
+
+ $in = "some stimulus" ;
+ $out = '' ;
+ $t->time( 5 )
+ pump $h until $out =~ /expected response/ || $t->is_expired ;
+
+Timers and timeouts that are reset get started by start() and
+pump(). Timers change state only in pump(). Since run() and
+finish() both call pump(), they act like pump() with repect to
+timers.
+
+Timers and timeouts have three states: reset, running, and expired.
+Setting the timeout value resets the timer, as does calling
+the reset() method. The start() method starts (or restarts) a
+timer with the most recently set time value, no matter what state
+it's in.
+
+=head2 Time values
+
+All time values are in seconds. Times may be specified as integer or
+floating point seconds, optionally preceded by puncuation-separated
+days, hours, and minutes.\
+
+Examples:
+
+ 1 1 second
+ 1.1 1.1 seconds
+ 60 60 seconds
+ 1:0 1 minute
+ 1:1 1 minute, 1 second
+ 1:90 2 minutes, 30 seconds
+ 1:2:3:4.5 1 day, 2 hours, 3 minutes, 4.5 seconds
+
+Absolute date/time strings are *not* accepted: year, month and
+day-of-month parsing is not available (patches welcome :-).
+
+=head2 Interval fudging
+
+When calculating an end time from a start time and an interval, IPC::Run::Timer
+instances add a little fudge factor. This is to ensure that no time will
+expire before the interval is up.
+
+First a little background. Time is sampled in discrete increments. We'll
+call the
+exact moment that the reported time increments from one interval to the
+next a tick, and the interval between ticks as the time period. Here's
+a diagram of three ticks and the periods between them:
+
+
+ -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
+ ^ ^ ^
+ |<--- period 0 ---->|<--- period 1 ---->|
+ | | |
+ tick 0 tick 1 tick 2
+
+To see why the fudge factor is necessary, consider what would happen
+when a timer with an interval of 1 second is started right at the end of
+period 0:
+
+
+ -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
+ ^ ^ ^ ^
+ | | | |
+ | | | |
+ tick 0 |tick 1 tick 2
+ |
+ start $t
+
+Assuming that check() is called many times per period, then the timer
+is likely to expire just after tick 1, since the time reported will have
+lept from the value '0' to the value '1':
+
+ -0-0-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-1-1-2-...
+ ^ ^ ^ ^ ^
+ | | | | |
+ | | | | |
+ tick 0 |tick 1| tick 2
+ | |
+ start $t |
+ |
+ check $t
+
+Adding a fudge of '1' in this example means that the timer is guaranteed
+not to expire before tick 2.
+
+The fudge is not added to an interval of '0'.
+
+This means that intervals guarantee a minimum interval. Given that
+the process running perl may be suspended for some period of time, or that
+it gets busy doing something time-consuming, there are no other guarantees on
+how long it will take a timer to expire.
+
+=head1 SUBCLASSING
+
+INCOMPATIBLE CHANGE: Due to the awkwardness introduced by ripping
+pseudohashes out of Perl, this class I<no longer> uses the fields
+pragma.
+
+=head1 FUNCTIONS & METHODS
+
+=over
+
+=cut ;
+
+use strict ;
+use Carp ;
+use Fcntl ;
+use Symbol ;
+use UNIVERSAL qw( isa ) ;
+use Exporter ;
+use vars qw( @EXPORT_OK %EXPORT_TAGS @ISA ) ;
+
+@EXPORT_OK = qw(
+ check
+ end_time
+ exception
+ expire
+ interval
+ is_expired
+ is_reset
+ is_running
+ name
+ reset
+ start
+
+ timeout
+ timer
+) ;
+
+%EXPORT_TAGS = ( 'all' => \@EXPORT_OK ) ;
+
+@ISA = qw( Exporter ) ;
+
+require IPC::Run ;
+use IPC::Run::Debug ;
+
+##
+## Some helpers
+##
+my $resolution = 1 ;
+
+sub _parse_time {
+ for ( $_[0] ) {
+ return $_ unless defined $_ ;
+ return $_ if /^\d*(?:\.\d*)?$/ ;
+
+ my @f = reverse split( /[^\d\.]+/i ) ;
+ croak "IPC::Run: invalid time string '$_'" unless @f <= 4 ;
+ my ( $s, $m, $h, $d ) = @f ;
+ return
+ ( (
+ ( $d || 0 ) * 24
+ + ( $h || 0 ) ) * 60
+ + ( $m || 0 ) ) * 60
+ + ( $s || 0 ) ;
+ }
+}
+
+
+sub _calc_end_time {
+ my IPC::Run::Timer $self = shift ;
+
+ my $interval = $self->interval ;
+ $interval += $resolution if $interval ;
+
+ $self->end_time( $self->start_time + $interval ) ;
+}
+
+
+=item timer
+
+A constructor function (not method) of IPC::Run::Timer instances:
+
+ $t = timer( 5 ) ;
+ $t = timer( 5, name => 'stall timer', debug => 1 ) ;
+
+ $t = timer ;
+ $t->interval( 5 ) ;
+
+ run ..., $t ;
+ run ..., $t = timer( 5 ) ;
+
+This convenience function is a shortened spelling of
+
+ IPC::Run::Timer->new( ... ) ;
+
+. It returns a timer in the reset state with a given interval.
+
+If an exception is provided, it will be thrown when the timer notices that
+it has expired (in check()). The name is for debugging usage, if you plan on
+having multiple timers around. If no name is provided, a name like "timer #1"
+will be provided.
+
+=cut
+
+sub timer {
+ return IPC::Run::Timer->new( @_ ) ;
+}
+
+
+=item timeout
+
+A constructor function (not method) of IPC::Run::Timer instances:
+
+ $t = timeout( 5 ) ;
+ $t = timeout( 5, exception => "kablooey" ) ;
+ $t = timeout( 5, name => "stall", exception => "kablooey" ) ;
+
+ $t = timeout ;
+ $t->interval( 5 ) ;
+
+ run ..., $t ;
+ run ..., $t = timeout( 5 ) ;
+
+A This convenience function is a shortened spelling of
+
+ IPC::Run::Timer->new( exception => "IPC::Run: timeout ...", ... ) ;
+
+. It returns a timer in the reset state that will throw an
+exception when it expires.
+
+Takes the same parameters as L</timer>, any exception passed in overrides
+the default exception.
+
+=cut
+
+sub timeout {
+ my $t = IPC::Run::Timer->new( @_ ) ;
+ $t->exception( "IPC::Run: timeout on " . $t->name )
+ unless defined $t->exception ;
+ return $t ;
+}
+
+
+=item new
+
+ IPC::Run::Timer->new() ;
+ IPC::Run::Timer->new( 5 ) ;
+ IPC::Run::Timer->new( 5, exception => 'kablooey' ) ;
+
+Constructor. See L</timer> for details.
+
+=cut
+
+my $timer_counter ;
+
+
+sub new {
+ my $class = shift ;
+ $class = ref $class || $class ;
+
+ my IPC::Run::Timer $self = bless {}, $class;
+
+ $self->{STATE} = 0 ;
+ $self->{DEBUG} = 0 ;
+ $self->{NAME} = "timer #" . ++$timer_counter ;
+
+ while ( @_ ) {
+ my $arg = shift ;
+ if ( $arg =~ /^(?:\d+[^\a\d]){0,3}\d*(?:\.\d*)?$/ ) {
+ $self->interval( $arg ) ;
+ }
+ elsif ( $arg eq 'exception' ) {
+ $self->exception( shift ) ;
+ }
+ elsif ( $arg eq 'name' ) {
+ $self->name( shift ) ;
+ }
+ elsif ( $arg eq 'debug' ) {
+ $self->debug( shift ) ;
+ }
+ else {
+ croak "IPC::Run: unexpected parameter '$arg'" ;
+ }
+ }
+
+ _debug $self->name . ' constructed'
+ if $self->{DEBUG} || _debugging_details ;
+
+ return $self ;
+}
+
+=item check
+
+ check $t ;
+ check $t, $now ;
+ $t->check ;
+
+Checks to see if a timer has expired since the last check. Has no effect
+on non-running timers. This will throw an exception if one is defined.
+
+IPC::Run::pump() calls this routine for any timers in the harness.
+
+You may pass in a version of now, which is useful in case you have
+it lying around or you want to check several timers with a consistent
+concept of the current time.
+
+Returns the time left before end_time or 0 if end_time is no longer
+in the future or the timer is not running
+(unless, of course, check() expire()s the timer and this
+results in an exception being thrown).
+
+Returns undef if the timer is not running on entry, 0 if check() expires it,
+and the time left if it's left running.
+
+=cut
+
+sub check {
+ my IPC::Run::Timer $self = shift ;
+ return undef if ! $self->is_running ;
+ return 0 if $self->is_expired ;
+
+ my ( $now ) = @_ ;
+ $now = _parse_time( $now ) ;
+ $now = time unless defined $now ;
+
+ _debug(
+ "checking ", $self->name, " (end time ", $self->end_time, ") at ", $now
+ ) if $self->{DEBUG} || _debugging_details ;
+
+ my $left = $self->end_time - $now ;
+ return $left if $left > 0 ;
+
+ $self->expire ;
+ return 0 ;
+}
+
+
+=item debug
+
+Sets/gets the current setting of the debugging flag for this timer. This
+has no effect if debugging is not enabled for the current harness.
+
+=cut
+
+
+sub debug {
+ my IPC::Run::Timer $self = shift ;
+ $self->{DEBUG} = shift if @_ ;
+ return $self->{DEBUG} ;
+}
+
+
+=item end_time
+
+ $et = $t->end_time ;
+ $et = end_time $t ;
+
+ $t->end_time( time + 10 ) ;
+
+Returns the time when this timer will or did expire. Even if this time is
+in the past, the timer may not be expired, since check() may not have been
+called yet.
+
+Note that this end_time is not start_time($t) + interval($t), since some
+small extra amount of time is added to make sure that the timer does not
+expire before interval() elapses. If this were not so, then
+
+Changing end_time() while a timer is running will set the expiration time.
+Changing it while it is expired has no affect, since reset()ing a timer always
+clears the end_time().
+
+=cut
+
+
+sub end_time {
+ my IPC::Run::Timer $self = shift ;
+ if ( @_ ) {
+ $self->{END_TIME} = shift ;
+ _debug $self->name, ' end_time set to ', $self->{END_TIME}
+ if $self->{DEBUG} > 2 || _debugging_details ;
+ }
+ return $self->{END_TIME} ;
+}
+
+
+=item exception
+
+ $x = $t->exception ;
+ $t->exception( $x ) ;
+ $t->exception( undef ) ;
+
+Sets/gets the exception to throw, if any. 'undef' means that no
+exception will be thrown. Exception does not need to be a scalar: you
+may ask that references be thrown.
+
+=cut
+
+
+sub exception {
+ my IPC::Run::Timer $self = shift ;
+ if ( @_ ) {
+ $self->{EXCEPTION} = shift ;
+ _debug $self->name, ' exception set to ', $self->{EXCEPTION}
+ if $self->{DEBUG} || _debugging_details ;
+ }
+ return $self->{EXCEPTION} ;
+}
+
+
+=item interval
+
+ $i = interval $t ;
+ $i = $t->interval ;
+ $t->interval( $i ) ;
+
+Sets the interval. Sets the end time based on the start_time() and the
+interval (and a little fudge) if the timer is running.
+
+=cut
+
+sub interval {
+ my IPC::Run::Timer $self = shift ;
+ if ( @_ ) {
+ $self->{INTERVAL} = _parse_time( shift ) ;
+ _debug $self->name, ' interval set to ', $self->{INTERVAL}
+ if $self->{DEBUG} > 2 || _debugging_details ;
+
+ $self->_calc_end_time if $self->state ;
+ }
+ return $self->{INTERVAL} ;
+}
+
+
+=item expire
+
+ expire $t ;
+ $t->expire ;
+
+Sets the state to expired (undef).
+Will throw an exception if one
+is defined and the timer was not already expired. You can expire a
+reset timer without starting it.
+
+=cut
+
+
+sub expire {
+ my IPC::Run::Timer $self = shift ;
+ if ( defined $self->state ) {
+ _debug $self->name . ' expired'
+ if $self->{DEBUG} || _debugging ;
+
+ $self->state( undef ) ;
+ croak $self->exception if $self->exception ;
+ }
+ return undef ;
+}
+
+
+=item is_running
+
+=cut
+
+
+sub is_running {
+ my IPC::Run::Timer $self = shift ;
+ return $self->state ? 1 : 0 ;
+}
+
+
+=item is_reset
+
+=cut
+
+sub is_reset {
+ my IPC::Run::Timer $self = shift ;
+ return defined $self->state && $self->state == 0 ;
+}
+
+
+=item is_expired
+
+=cut
+
+sub is_expired {
+ my IPC::Run::Timer $self = shift ;
+ return ! defined $self->state ;
+}
+
+=item name
+
+Sets/gets this timer's name. The name is only used for debugging
+purposes so you can tell which freakin' timer is doing what.
+
+=cut
+
+sub name {
+ my IPC::Run::Timer $self = shift ;
+
+ $self->{NAME} = shift if @_ ;
+ return defined $self->{NAME}
+ ? $self->{NAME}
+ : defined $self->{EXCEPTION}
+ ? 'timeout'
+ : 'timer' ;
+}
+
+
+=item reset
+
+ reset $t ;
+ $t->reset ;
+
+Resets the timer to the non-running, non-expired state and clears
+the end_time().
+
+=cut
+
+sub reset {
+ my IPC::Run::Timer $self = shift ;
+ $self->state( 0 ) ;
+ $self->end_time( undef ) ;
+ _debug $self->name . ' reset'
+ if $self->{DEBUG} || _debugging ;
+
+ return undef ;
+}
+
+
+=item start
+
+ start $t ;
+ $t->start ;
+ start $t, $interval ;
+ start $t, $interval, $now ;
+
+Starts or restarts a timer. This always sets the start_time. It sets the
+end_time based on the interval if the timer is running or if no end time
+has been set.
+
+You may pass an optional interval or current time value.
+
+Not passing a defined interval causes the previous interval setting to be
+re-used unless the timer is reset and an end_time has been set
+(an exception is thrown if no interval has been set).
+
+Not passing a defined current time value causes the current time to be used.
+
+Passing a current time value is useful if you happen to have a time value
+lying around or if you want to make sure that several timers are started
+with the same concept of start time. You might even need to lie to an
+IPC::Run::Timer, occasionally.
+
+=cut
+
+sub start {
+ my IPC::Run::Timer $self = shift ;
+
+ my ( $interval, $now ) = map { _parse_time( $_ ) } @_ ;
+ $now = _parse_time( $now ) ;
+ $now = time unless defined $now ;
+
+ $self->interval( $interval ) if defined $interval ;
+
+ ## start()ing a running or expired timer clears the end_time, so that the
+ ## interval is used. So does specifying an interval.
+ $self->end_time( undef ) if ! $self->is_reset || $interval ;
+
+ croak "IPC::Run: no timer interval or end_time defined for " . $self->name
+ unless defined $self->interval || defined $self->end_time ;
+
+ $self->state( 1 ) ;
+ $self->start_time( $now ) ;
+ ## The "+ 1" is in case the START_TIME was sampled at the end of a
+ ## tick (which are one second long in this module).
+ $self->_calc_end_time
+ unless defined $self->end_time ;
+
+ _debug(
+ $self->name, " started at ", $self->start_time,
+ ", with interval ", $self->interval, ", end_time ", $self->end_time
+ ) if $self->{DEBUG} || _debugging ;
+ return undef ;
+}
+
+
+=item start_time
+
+Sets/gets the start time, in seconds since the epoch. Setting this manually
+is a bad idea, it's better to call L</start>() at the correct time.
+
+=cut
+
+
+sub start_time {
+ my IPC::Run::Timer $self = shift ;
+ if ( @_ ) {
+ $self->{START_TIME} = _parse_time( shift ) ;
+ _debug $self->name, ' start_time set to ', $self->{START_TIME}
+ if $self->{DEBUG} > 2 || _debugging ;
+ }
+
+ return $self->{START_TIME} ;
+}
+
+
+=item state
+
+ $s = state $t ;
+ $t->state( $s ) ;
+
+Get/Set the current state. Only use this if you really need to transfer the
+state to/from some variable.
+Use L</expire>, L</start>, L</reset>, L</is_expired>, L</is_running>,
+L</is_reset>.
+
+Note: Setting the state to 'undef' to expire a timer will not throw an
+exception.
+
+=cut
+
+sub state {
+ my IPC::Run::Timer $self = shift ;
+ if ( @_ ) {
+ $self->{STATE} = shift ;
+ _debug $self->name, ' state set to ', $self->{STATE}
+ if $self->{DEBUG} > 2 || _debugging ;
+ }
+ return $self->{STATE} ;
+}
+
+
+=head1 TODO
+
+use Time::HiRes ; if it's present.
+
+Add detection and parsing of [[[HH:]MM:]SS formatted times and intervals.
+
+=head1 AUTHOR
+
+Barrie Slaymaker <barries@slaysys.com>
+
+=cut
+
+1 ;
--- /dev/null
+package IPC::Run::Win32Helper ;
+
+=head1 NAME
+
+IPC::Run::Win32Helper - helper routines for IPC::Run on Win32 platforms.
+
+=head1 SYNOPSIS
+
+ use IPC::Run::Win32Helper; # Exports all by default
+
+=head1 DESCRIPTION
+
+IPC::Run needs to use sockets to redirect subprocess I/O so that the select() loop
+will work on Win32. This seems to only work on WinNT and Win2K at this time, not
+sure if it will ever work on Win95 or Win98. If you have experience in this area, please
+contact me at barries@slaysys.com, thanks!.
+
+=cut
+
+@ISA = qw( Exporter ) ;
+
+@EXPORT = qw(
+ win32_spawn
+ win32_parse_cmd_line
+ _dont_inherit
+ _inherit
+) ;
+
+use strict ;
+use Carp ;
+use IO::Handle ;
+#use IPC::Open3 ();
+require POSIX ;
+
+use Text::ParseWords ;
+use Win32::Process ;
+use IPC::Run::Debug;
+## REMOVE OSFHandleOpen
+use Win32API::File qw(
+ FdGetOsFHandle
+ SetHandleInformation
+ HANDLE_FLAG_INHERIT
+ INVALID_HANDLE_VALUE
+) ;
+
+## Takes an fd or a GLOB ref, never never never a Win32 handle.
+sub _dont_inherit {
+ for ( @_ ) {
+ next unless defined $_ ;
+ my $fd = $_ ;
+ $fd = fileno $fd if ref $fd ;
+ _debug "disabling inheritance of ", $fd if _debugging_details ;
+ my $osfh = FdGetOsFHandle $fd ;
+ croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ;
+
+ SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 0 ) ;
+ }
+}
+
+sub _inherit { #### REMOVE
+ for ( @_ ) { #### REMOVE
+ next unless defined $_ ; #### REMOVE
+ my $fd = $_ ; #### REMOVE
+ $fd = fileno $fd if ref $fd ; #### REMOVE
+ _debug "enabling inheritance of ", $fd if _debugging_details ; #### REMOVE
+ my $osfh = FdGetOsFHandle $fd ; #### REMOVE
+ croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ; #### REMOVE
+ #### REMOVE
+ SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, 1 ) ; #### REMOVE
+ } #### REMOVE
+} #### REMOVE
+ #### REMOVE
+#sub _inherit {
+# for ( @_ ) {
+# next unless defined $_ ;
+# my $osfh = GetOsFHandle $_ ;
+# croak $^E if ! defined $osfh || $osfh == INVALID_HANDLE_VALUE ;
+# SetHandleInformation( $osfh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ) ;
+# }
+#}
+
+=head1 FUNCTIONS
+
+=over
+
+=cut
+
+=item optimize()
+
+Most common incantations of C<run()> (I<not> C<harness()>, C<start()>,
+or C<finish()>) now use temporary files to redirect input and output
+instead of pumper processes.
+
+Temporary files are used when sending to child processes if input is
+taken from a scalar with no filter subroutines. This is the only time
+we can assume that the parent is not interacting with the child's
+redirected input as it runs.
+
+Temporary files are used when receiving from children when output is
+to a scalar or subroutine with or without filters, but only if
+the child in question closes its inputs or takes input from
+unfiltered SCALARs or named files. Normally, a child inherits its STDIN
+from its parent; to close it, use "0<&-" or the C<noinherit => 1> option.
+If data is sent to the child from CODE refs, filehandles or from
+scalars through filters than the child's outputs will not be optimized
+because C<optimize()> assumes the parent is interacting with the child.
+It is ok if the output is filtered or handled by a subroutine, however.
+
+This assumes that all named files are real files (as opposed to named
+pipes) and won't change; and that a process is not communicating with
+the child indirectly (through means not visible to IPC::Run).
+These can be an invalid assumptions, but are the 99% case.
+Write me if you need an option to enable or disable optimizations; I
+suspect it will work like the C<binary()> modifier.
+
+To detect cases that you might want to optimize by closing inputs, try
+setting the C<IPCRUNDEBUG> environment variable to the special C<notopt>
+value:
+
+ C:> set IPCRUNDEBUG=notopt
+ C:> my_app_that_uses_IPC_Run.pl
+
+=item optimizer() rationalizations
+
+Only for that limited case can we be sure that it's ok to batch all the
+input in to a temporary file. If STDIN is from a SCALAR or from a named
+file or filehandle (again, only in C<run()>), then outputs to CODE refs
+are also assumed to be safe enough to batch through a temp file,
+otherwise only outputs to SCALAR refs are batched. This can cause a bit
+of grief if the parent process benefits from or relies on a bit of
+"early returns" coming in before the child program exits. As long as
+the output is redirected to a SCALAR ref, this will not be visible.
+When output is redirected to a subroutine or (deprecated) filters, the
+subroutine will not get any data until after the child process exits,
+and it is likely to get bigger chunks of data at once.
+
+The reason for the optimization is that, without it, "pumper" processes
+are used to overcome the inconsistancies of the Win32 API. We need to
+use anonymous pipes to connect to the child processes' stdin, stdout,
+and stderr, yet select() does not work on these. select() only works on
+sockets on Win32. So for each redirected child handle, there is
+normally a "pumper" process that connects to the parent using a
+socket--so the parent can select() on that fd--and to the child on an
+anonymous pipe--so the child can read/write a pipe.
+
+Using a socket to connect directly to the child (as at least one MSDN
+article suggests) seems to cause the trailing output from most children
+to be lost. I think this is because child processes rarely close their
+stdout and stderr explicitly, and the winsock dll does not seem to flush
+output when a process that uses it exits without explicitly closing
+them.
+
+Because of these pumpers and the inherent slowness of Win32
+CreateProcess(), child processes with redirects are quite slow to
+launch; so this routine looks for the very common case of
+reading/writing to/from scalar references in a run() routine and
+converts such reads and writes in to temporary file reads and writes.
+
+Such files are marked as FILE_ATTRIBUTE_TEMPORARY to increase speed and
+as FILE_FLAG_DELETE_ON_CLOSE so it will be cleaned up when the child
+process exits (for input files). The user's default permissions are
+used for both the temporary files and the directory that contains them,
+hope your Win32 permissions are secure enough for you. Files are
+created with the Win32API::File defaults of
+FILE_SHARE_READ|FILE_SHARE_WRITE.
+
+Setting the debug level to "details" or "gory" will give detailed
+information about the optimization process; setting it to "basic" or
+higher will tell whether or not a given call is optimized. Setting
+it to "notopt" will highligh those calls that aren't optimized.
+
+=cut
+
+sub optimize {
+ my ( $h ) = @_;
+
+ my @kids = @{$h->{KIDS}};
+
+ my $saw_pipe;
+
+ my ( $ok_to_optimize_outputs, $veto_output_optimization );
+
+ for my $kid ( @kids ) {
+ ( $ok_to_optimize_outputs, $veto_output_optimization ) = ()
+ unless $saw_pipe;
+
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over ok of non-SCALAR output optimization"
+ if _debugging_details && $ok_to_optimize_outputs;
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) STDIN piped, carrying over veto of non-SCALAR output optimization"
+ if _debugging_details && $veto_output_optimization;
+
+ if ( $h->{noinherit} && ! $ok_to_optimize_outputs ) {
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) STDIN not inherited from parent oking non-SCALAR output optimization"
+ if _debugging_details && $ok_to_optimize_outputs;
+ $ok_to_optimize_outputs = 1;
+ }
+
+ for ( @{$kid->{OPS}} ) {
+ if ( substr( $_->{TYPE}, 0, 1 ) eq "<" ) {
+ if ( $_->{TYPE} eq "<" ) {
+ if ( @{$_->{FILTERS}} > 1 ) {
+ ## Can't assume that the filters are idempotent.
+ }
+ elsif ( ref $_->{SOURCE} eq "SCALAR"
+ || ref $_->{SOURCE} eq "GLOB"
+ || UNIVERSAL::isa( $_, "IO::Handle" )
+ ) {
+ if ( $_->{KFD} == 0 ) {
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) 0$_->{TYPE}",
+ ref $_->{SOURCE},
+ ", ok to optimize outputs"
+ if _debugging_details;
+ $ok_to_optimize_outputs = 1;
+ }
+ $_->{SEND_THROUGH_TEMP_FILE} = 1;
+ next;
+ }
+ elsif ( ! ref $_->{SOURCE} && defined $_->{SOURCE} ) {
+ if ( $_->{KFD} == 0 ) {
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) 0<$_->{SOURCE}, ok to optimize outputs",
+ if _debugging_details;
+ $ok_to_optimize_outputs = 1;
+ }
+ next;
+ }
+ }
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) ",
+ $_->{KFD},
+ $_->{TYPE},
+ defined $_->{SOURCE}
+ ? ref $_->{SOURCE} ? ref $_->{SOURCE}
+ : $_->{SOURCE}
+ : defined $_->{FILENAME}
+ ? $_->{FILENAME}
+ : "",
+ @{$_->{FILTERS}} > 1 ? " with filters" : (),
+ ", VETOING output opt."
+ if _debugging_details || _debugging_not_optimized;
+ $veto_output_optimization = 1;
+ }
+ elsif ( $_->{TYPE} eq "close" && $_->{KFD} == 0 ) {
+ $ok_to_optimize_outputs = 1;
+ _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&-, ok to optimize outputs"
+ if _debugging_details;
+ }
+ elsif ( $_->{TYPE} eq "dup" && $_->{KFD2} == 0 ) {
+ $veto_output_optimization = 1;
+ _debug "Win32 optimizer: (kid $kid->{NUM}) saw 0<&$_->{KFD2}, VETOING output opt."
+ if _debugging_details || _debugging_not_optimized;
+ }
+ elsif ( $_->{TYPE} eq "|" ) {
+ $saw_pipe = 1;
+ }
+ }
+
+ if ( ! $ok_to_optimize_outputs && ! $veto_output_optimization ) {
+ _debug
+ "Win32 optimizer: (kid $kid->{NUM}) child STDIN not redirected, VETOING non-SCALAR output opt."
+ if _debugging_details || _debugging_not_optimized;
+ $veto_output_optimization = 1;
+ }
+
+ if ( $ok_to_optimize_outputs && $veto_output_optimization ) {
+ $ok_to_optimize_outputs = 0;
+ _debug "Win32 optimizer: (kid $kid->{NUM}) non-SCALAR output optimizations VETOed"
+ if _debugging_details || _debugging_not_optimized;
+ }
+
+ ## SOURCE/DEST ARRAY means it's a filter.
+ ## TODO: think about checking to see if the final input/output of
+ ## a filter chain (an ARRAY SOURCE or DEST) is a scalar...but
+ ## we may be deprecating filters.
+
+ for ( @{$kid->{OPS}} ) {
+ if ( $_->{TYPE} eq ">" ) {
+ if ( ref $_->{DEST} eq "SCALAR"
+ || (
+ ( @{$_->{FILTERS}} > 1
+ || ref $_->{DEST} eq "CODE"
+ || ref $_->{DEST} eq "ARRAY" ## Filters?
+ )
+ && ( $ok_to_optimize_outputs && ! $veto_output_optimization )
+ )
+ ) {
+ $_->{RECV_THROUGH_TEMP_FILE} = 1;
+ next;
+ }
+ _debug
+ "Win32 optimizer: NOT optimizing (kid $kid->{NUM}) ",
+ $_->{KFD},
+ $_->{TYPE},
+ defined $_->{DEST}
+ ? ref $_->{DEST} ? ref $_->{DEST}
+ : $_->{SOURCE}
+ : defined $_->{FILENAME}
+ ? $_->{FILENAME}
+ : "",
+ @{$_->{FILTERS}} ? " with filters" : (),
+ if _debugging_details;
+ }
+ }
+ }
+
+}
+
+=item win32_parse_cmd_line
+
+ @words = win32_parse_cmd_line( q{foo bar 'baz baz' "bat bat"} ) ;
+
+returns 4 words. This parses like the bourne shell (see
+the bit about shellwords() in L<Text::ParseWords>), assuming we're
+trying to be a little cross-platform here. The only difference is
+that "\" is *not* treated as an escape except when it precedes
+punctuation, since it's used all over the place in DOS path specs.
+
+TODO: globbing? probably not (it's unDOSish).
+
+TODO: shebang emulation? Probably, but perhaps that should be part
+of Run.pm so all spawned processes get the benefit.
+
+LIMITATIONS: shellwords dies silently on malformed input like
+
+ a\"
+
+=cut
+
+sub win32_parse_cmd_line {
+ my $line = shift ;
+ $line =~ s{(\\[\w\s])}{\\$1}g ;
+ return shellwords $line ;
+}
+
+
+=item win32_spawn
+
+Spawns a child process, possibly with STDIN, STDOUT, and STDERR (file descriptors 0, 1, and 2, respectively) redirected.
+
+B<LIMITATIONS>.
+
+Cannot redirect higher file descriptors due to lack of support for this in the
+Win32 environment.
+
+This can be worked around by marking a handle as inheritable in the
+parent (or leaving it marked; this is the default in perl), obtaining it's
+Win32 handle with C<Win32API::GetOSFHandle(FH)> or
+C<Win32API::FdGetOsFHandle($fd)> and passing it to the child using the command
+line, the environment, or any other IPC mechanism (it's a plain old integer).
+The child can then use C<OsFHandleOpen()> or C<OsFHandleOpenFd()> and possibly
+C<<open FOO ">&BAR">> or C<<open FOO ">&$fd>> as need be. Ach, the pain!
+
+Remember to check the Win32 handle against INVALID_HANDLE_VALUE.
+
+=cut
+
+sub _save {
+ my ( $saved, $saved_as, $fd ) = @_ ;
+
+ ## We can only save aside the original fds once.
+ return if exists $saved->{$fd} ;
+
+ my $saved_fd = IPC::Run::_dup( $fd ) ;
+ _dont_inherit $saved_fd ;
+
+ $saved->{$fd} = $saved_fd ;
+ $saved_as->{$saved_fd} = $fd ;
+
+ _dont_inherit $saved->{$fd} ;
+}
+
+sub _dup2_gently {
+ my ( $saved, $saved_as, $fd1, $fd2 ) = @_ ;
+ _save $saved, $saved_as, $fd2 ;
+
+ if ( exists $saved_as->{$fd2} ) {
+ ## The target fd is colliding with a saved-as fd, gotta bump
+ ## the saved-as fd to another fd.
+ my $orig_fd = delete $saved_as->{$fd2} ;
+ my $saved_fd = IPC::Run::_dup( $fd2 ) ;
+ _dont_inherit $saved_fd ;
+
+ $saved->{$orig_fd} = $saved_fd ;
+ $saved_as->{$saved_fd} = $orig_fd ;
+ }
+ _debug "moving $fd1 to kid's $fd2" if _debugging_details ;
+ IPC::Run::_dup2_rudely( $fd1, $fd2 ) ;
+}
+
+sub win32_spawn {
+ my ( $cmd, $ops) = @_ ;
+
+ ## NOTE: The debug pipe write handle is passed to pump processes as STDOUT.
+ ## and is not to the "real" child process, since they would not know
+ ## what to do with it...unlike Unix, we have no code executing in the
+ ## child before the "real" child is exec()ed.
+
+ my %saved ; ## Map of parent's orig fd -> saved fd
+ my %saved_as ; ## Map of parent's saved fd -> orig fd, used to
+ ## detect collisions between a KFD and the fd a
+ ## parent's fd happened to be saved to.
+
+ for my $op ( @$ops ) {
+ _dont_inherit $op->{FD} if defined $op->{FD} ;
+
+ if ( defined $op->{KFD} && $op->{KFD} > 2 ) {
+ ## TODO: Detect this in harness()
+ ## TODO: enable temporary redirections if ever necessary, not
+ ## sure why they would be...
+ ## 4>&1 1>/dev/null 1>&4 4>&-
+ croak "Can't redirect fd #", $op->{KFD}, " on Win32" ;
+ }
+
+ ## This is very similar logic to IPC::Run::_do_kid_and_exit().
+ if ( defined $op->{TFD} ) {
+ unless ( $op->{TFD} == $op->{KFD} ) {
+ _dup2_gently \%saved, \%saved_as, $op->{TFD}, $op->{KFD} ;
+ _dont_inherit $op->{TFD} ;
+ }
+ }
+ elsif ( $op->{TYPE} eq "dup" ) {
+ _dup2_gently \%saved, \%saved_as, $op->{KFD1}, $op->{KFD2}
+ unless $op->{KFD1} == $op->{KFD2} ;
+ }
+ elsif ( $op->{TYPE} eq "close" ) {
+ _save \%saved, \%saved_as, $op->{KFD} ;
+ IPC::Run::_close( $op->{KFD} ) ;
+ }
+ elsif ( $op->{TYPE} eq "init" ) {
+ ## TODO: detect this in harness()
+ croak "init subs not allowed on Win32" ;
+ }
+ }
+
+ my $process ;
+ my $cmd_line = join " ", map {
+ ( my $s = $_ ) =~ s/"/"""/g;
+ $s = qq{"$s"} if /["\s]/;
+ $s ;
+ } @$cmd ;
+
+ _debug "cmd line: ", $cmd_line
+ if _debugging;
+
+ Win32::Process::Create(
+ $process,
+ $cmd->[0],
+ $cmd_line,
+ 1, ## Inherit handles
+ NORMAL_PRIORITY_CLASS,
+ ".",
+ ) or croak "$!: Win32::Process::Create()" ;
+
+ for my $orig_fd ( keys %saved ) {
+ IPC::Run::_dup2_rudely( $saved{$orig_fd}, $orig_fd ) ;
+ IPC::Run::_close( $saved{$orig_fd} ) ;
+ }
+
+ return ( $process->GetProcessID(), $process ) ;
+}
+
+
+=back
+
+=head1 AUTHOR
+
+Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
+
+=head1 COPYRIGHT
+
+Copyright 2001, Barrie Slaymaker, All Rights Reserved.
+
+You may use this under the terms of either the GPL 2.0 ir the Artistic License.
+
+=cut
+
+1 ;
--- /dev/null
+package IPC::Run::Win32IO;
+
+=head1 NAME
+
+IPC::Run::Win32IO - helper routines for IPC::Run on Win32 platforms.
+
+=head1 SYNOPSIS
+
+ use IPC::Run::Win32IO; # Exports all by default
+
+=head1 DESCRIPTION
+
+IPC::Run needs to use sockets to redirect subprocess I/O so that the select()
+loop will work on Win32. This seems to only work on WinNT and Win2K at this
+time, not sure if it will ever work on Win95 or Win98. If you have experience
+in this area, please contact me at barries@slaysys.com, thanks!.
+
+=cut
+
+=head1 DESCRIPTION
+
+A specialized IO class used on Win32.
+
+=cut
+
+use strict ;
+use Carp ;
+use IO::Handle ;
+use Socket ;
+require POSIX ;
+
+use Socket qw( IPPROTO_TCP TCP_NODELAY ) ;
+use Symbol ;
+use Text::ParseWords ;
+use Win32::Process ;
+use IPC::Run::Debug qw( :default _debugging_level );
+use IPC::Run::Win32Helper qw( _inherit _dont_inherit );
+use Fcntl qw( O_TEXT O_RDONLY );
+
+use base qw( IPC::Run::IO );
+my @cleanup_fields;
+BEGIN {
+ ## These fields will be set to undef in _cleanup to close
+ ## the handles.
+ @cleanup_fields = (
+ 'SEND_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
+ 'RECV_THROUGH_TEMP_FILE', ## Set by WinHelper::optimize()
+ 'TEMP_FILE_NAME', ## The name of the temp file, needed for
+ ## error reporting / debugging only.
+
+ 'PARENT_HANDLE', ## The handle of the socket for the parent
+ 'PUMP_SOCKET_HANDLE', ## The socket handle for the pump
+ 'PUMP_PIPE_HANDLE', ## The anon pipe handle for the pump
+ 'CHILD_HANDLE', ## The anon pipe handle for the child
+
+ 'TEMP_FILE_HANDLE', ## The Win32 filehandle for the temp file
+ );
+}
+
+## REMOVE OSFHandleOpen
+use Win32API::File qw(
+ GetOsFHandle
+ OsFHandleOpenFd
+ OsFHandleOpen
+ FdGetOsFHandle
+ SetHandleInformation
+ SetFilePointer
+ HANDLE_FLAG_INHERIT
+ INVALID_HANDLE_VALUE
+
+ createFile
+ WriteFile
+ ReadFile
+ CloseHandle
+
+ FILE_ATTRIBUTE_TEMPORARY
+ FILE_FLAG_DELETE_ON_CLOSE
+ FILE_FLAG_WRITE_THROUGH
+
+ FILE_BEGIN
+) ;
+
+# FILE_ATTRIBUTE_HIDDEN
+# FILE_ATTRIBUTE_SYSTEM
+
+
+BEGIN {
+ ## Force AUTOLOADED constants to be, well, constant by getting them
+ ## to AUTOLOAD before compilation continues. Sigh.
+ () = (
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ HANDLE_FLAG_INHERIT,
+ INVALID_HANDLE_VALUE,
+ );
+}
+
+
+use constant temp_file_flags => (
+ FILE_ATTRIBUTE_TEMPORARY() |
+ FILE_FLAG_DELETE_ON_CLOSE() |
+ FILE_FLAG_WRITE_THROUGH()
+);
+
+# FILE_ATTRIBUTE_HIDDEN() |
+# FILE_ATTRIBUTE_SYSTEM() |
+my $tmp_file_counter;
+my $tmp_dir;
+
+sub _cleanup {
+ my IPC::Run::Win32IO $self = shift;
+ my ( $harness ) = @_;
+
+ $self->_recv_through_temp_file( $harness )
+ if $self->{RECV_THROUGH_TEMP_FILE};
+
+ CloseHandle( $self->{TEMP_FILE_HANDLE} )
+ if defined $self->{TEMP_FILE_HANDLE};
+
+ $self->{$_} = undef for @cleanup_fields;
+}
+
+
+sub _create_temp_file {
+ my IPC::Run::Win32IO $self = shift;
+
+ ## Create a hidden temp file that Win32 will delete when we close
+ ## it.
+ unless ( defined $tmp_dir ) {
+ $tmp_dir = File::Spec->catdir(
+ File::Spec->tmpdir, "IPC-Run.tmp"
+ );
+
+ ## Trust in the user's umask.
+ ## This could possibly be a security hole, perhaps
+ ## we should offer an option. Hmmmm, really, people coding
+ ## security conscious apps should audit this code and
+ ## tell me how to make it better. Nice cop-out :).
+ unless ( -d $tmp_dir ) {
+ mkdir $tmp_dir or croak "$!: $tmp_dir";
+ }
+ }
+
+ $self->{TEMP_FILE_NAME} = File::Spec->catfile(
+ ## File name is designed for easy sorting and not conflicting
+ ## with other processes. This should allow us to use "t"runcate
+ ## access in CreateFile in case something left some droppings
+ ## around (which should never happen because we specify
+ ## FLAG_DELETE_ON_CLOSE.
+ ## heh, belt and suspenders are better than bug reports; God forbid
+ ## that NT should ever crash before a temp file gets deleted!
+ $tmp_dir, sprintf "Win32io-%06d-%08d", $$, $tmp_file_counter++
+ );
+
+ $self->{TEMP_FILE_HANDLE} = createFile(
+ $self->{TEMP_FILE_NAME},
+ "trw", ## new, truncate, read, write
+ {
+ Flags => temp_file_flags,
+ },
+ ) or croak "Can't create temporary file, $self->{TEMP_FILE_NAME}: $^E";
+
+ $self->{TFD} = OsFHandleOpenFd $self->{TEMP_FILE_HANDLE}, 0;
+ $self->{FD} = undef;
+
+ _debug
+ "Win32 Optimizer: temp file (",
+ $self->{KFD},
+ $self->{TYPE},
+ $self->{TFD},
+ ", fh ",
+ $self->{TEMP_FILE_HANDLE},
+ "): ",
+ $self->{TEMP_FILE_NAME}
+ if _debugging_details;
+}
+
+
+sub _reset_temp_file_pointer {
+ my $self = shift;
+ SetFilePointer( $self->{TEMP_FILE_HANDLE}, 0, 0, FILE_BEGIN )
+ or confess "$^E seeking on (fd $self->{TFD}) $self->{TEMP_FILE_NAME} for kid's fd $self->{KFD}";
+}
+
+
+sub _send_through_temp_file {
+ my IPC::Run::Win32IO $self = shift;
+
+ _debug
+ "Win32 optimizer: optimizing "
+ . " $self->{KFD} $self->{TYPE} temp file instead of ",
+ ref $self->{SOURCE} || $self->{SOURCE}
+ if _debugging_details;
+
+ $self->_create_temp_file;
+
+ if ( defined ${$self->{SOURCE}} ) {
+ my $bytes_written = 0;
+ my $data_ref;
+ if ( $self->binmode ) {
+ $data_ref = $self->{SOURCE};
+ }
+ else {
+ my $data = ${$self->{SOURCE}}; # Ugh, a copy.
+ $data =~ s/(?<!\r)\n/\r\n/g;
+ $data_ref = \$data;
+ }
+
+ WriteFile(
+ $self->{TEMP_FILE_HANDLE},
+ $$data_ref,
+ 0, ## Write entire buffer
+ $bytes_written,
+ [], ## Not overlapped.
+ ) or croak
+ "$^E writing $self->{TEMP_FILE_NAME} for kid to read on fd $self->{KFD}";
+ _debug
+ "Win32 optimizer: wrote $bytes_written to temp file $self->{TEMP_FILE_NAME}"
+ if _debugging_data;
+
+ $self->_reset_temp_file_pointer;
+
+ }
+
+
+ _debug "Win32 optimizer: kid to read $self->{KFD} from temp file on $self->{TFD}"
+ if _debugging_details;
+}
+
+
+sub _init_recv_through_temp_file {
+ my IPC::Run::Win32IO $self = shift;
+
+ $self->_create_temp_file;
+}
+
+
+## TODO: USe the Win32 API in the select loop to see if the file has grown
+## and read it incrementally if it has.
+sub _recv_through_temp_file {
+ my IPC::Run::Win32IO $self = shift;
+
+ ## This next line kicks in if the run() never got to initting things
+ ## and needs to clean up.
+ return undef unless defined $self->{TEMP_FILE_HANDLE};
+
+ push @{$self->{FILTERS}}, sub {
+ my ( undef, $out_ref ) = @_;
+
+ return undef unless defined $self->{TEMP_FILE_HANDLE};
+
+ my $r;
+ my $s;
+ ReadFile(
+ $self->{TEMP_FILE_HANDLE},
+ $s,
+ 999_999, ## Hmmm, should read the size.
+ $r,
+ []
+ ) or croak "$^E reading from $self->{TEMP_FILE_NAME}";
+
+ _debug "ReadFile( $self->{TFD} ) = $r chars '$s'" if _debugging_data ;
+
+ return undef unless $r;
+
+ $s =~ s/\r\n/\n/g unless $self->binmode;
+
+ my $pos = pos $$out_ref;
+ $$out_ref .= $s;
+ pos( $out_ref ) = $pos;
+ return 1;
+ };
+
+ my ( $harness ) = @_;
+
+ $self->_reset_temp_file_pointer;
+
+ 1 while $self->_do_filters( $harness );
+
+ pop @{$self->{FILTERS}};
+
+ IPC::Run::_close( $self->{TFD} );
+}
+
+
+sub poll {
+ my IPC::Run::Win32IO $self = shift;
+
+ return if $self->{SEND_THROUGH_TEMP_FILE} || $self->{RECV_THROUGH_TEMP_FILE};
+
+ return $self->SUPER::poll( @_ );
+}
+
+
+## When threaded Perls get good enough, we should use threads here.
+## The problem with threaded perls is that they dup() all sorts of
+## filehandles and fds and don't allow sufficient control over
+## closing off the ones we don't want.
+
+sub _spawn_pumper {
+ my ( $stdin, $stdout, $debug_fd, $binmode, $child_label, @opts ) = @_ ;
+ my ( $stdin_fd, $stdout_fd ) = ( fileno $stdin, fileno $stdout ) ;
+
+ _debug "pumper stdin = ", $stdin_fd if _debugging_details;
+ _debug "pumper stdout = ", $stdout_fd if _debugging_details;
+ _inherit $stdin_fd, $stdout_fd, $debug_fd ;
+ my @I_options = map qq{"-I$_"}, @INC;
+
+ my $cmd_line = join( " ",
+ qq{"$^X"},
+ @I_options,
+ qw(-MIPC::Run::Win32Pump -e 1 ),
+## I'm using this clunky way of passing filehandles to the child process
+## in order to avoid some kind of premature closure of filehandles
+## problem I was having with VCP's test suite when passing them
+## via CreateProcess. All of the ## REMOVE code is stuff I'd like
+## to be rid of and the ## ADD code is what I'd like to use.
+ FdGetOsFHandle( $stdin_fd ), ## REMOVE
+ FdGetOsFHandle( $stdout_fd ), ## REMOVE
+ FdGetOsFHandle( $debug_fd ), ## REMOVE
+ $binmode ? 1 : 0,
+ $$, $^T, _debugging_level, qq{"$child_label"},
+ @opts
+ ) ;
+
+# open SAVEIN, "<&STDIN" or croak "$! saving STDIN" ; #### ADD
+# open SAVEOUT, ">&STDOUT" or croak "$! saving STDOUT" ; #### ADD
+# open SAVEERR, ">&STDERR" or croak "$! saving STDERR" ; #### ADD
+# _dont_inherit \*SAVEIN ; #### ADD
+# _dont_inherit \*SAVEOUT ; #### ADD
+# _dont_inherit \*SAVEERR ; #### ADD
+# open STDIN, "<&$stdin_fd" or croak "$! dup2()ing $stdin_fd (pumper's STDIN)" ; #### ADD
+# open STDOUT, ">&$stdout_fd" or croak "$! dup2()ing $stdout_fd (pumper's STDOUT)" ; #### ADD
+# open STDERR, ">&$debug_fd" or croak "$! dup2()ing $debug_fd (pumper's STDERR/debug_fd)" ; #### ADD
+
+ _debug "pump cmd line: ", $cmd_line if _debugging_details;
+
+ my $process ;
+ Win32::Process::Create(
+ $process,
+ $^X,
+ $cmd_line,
+ 1, ## Inherit handles
+ NORMAL_PRIORITY_CLASS,
+ ".",
+ ) or croak "$!: Win32::Process::Create()" ;
+
+# open STDIN, "<&SAVEIN" or croak "$! restoring STDIN" ; #### ADD
+# open STDOUT, ">&SAVEOUT" or croak "$! restoring STDOUT" ; #### ADD
+# open STDERR, ">&SAVEERR" or croak "$! restoring STDERR" ; #### ADD
+# close SAVEIN or croak "$! closing SAVEIN" ; #### ADD
+# close SAVEOUT or croak "$! closing SAVEOUT" ; #### ADD
+# close SAVEERR or croak "$! closing SAVEERR" ; #### ADD
+
+ close $stdin or croak "$! closing pumper's stdin in parent" ;
+ close $stdout or croak "$! closing pumper's stdout in parent" ;
+ # Don't close $debug_fd, we need it, as do other pumpers.
+
+ # Pause a moment to allow the child to get up and running and emit
+ # debug messages. This does not always work.
+ # select undef, undef, undef, 1 if _debugging_details ;
+
+ _debug "_spawn_pumper pid = ", $process->GetProcessID
+ if _debugging_data;
+}
+
+
+my $next_port = 2048 ;
+my $loopback = inet_aton "127.0.0.1" ;
+my $tcp_proto = getprotobyname('tcp');
+croak "$!: getprotobyname('tcp')" unless defined $tcp_proto ;
+
+sub _socket {
+ my ( $server ) = @_ ;
+ $server ||= gensym ;
+ my $client = gensym ;
+
+ my $listener = gensym ;
+ socket $listener, PF_INET, SOCK_STREAM, $tcp_proto
+ or croak "$!: socket()";
+ setsockopt $listener, SOL_SOCKET, SO_REUSEADDR, pack("l", 0)
+ or croak "$!: setsockopt()";
+
+ my $port ;
+ my @errors ;
+PORT_FINDER_LOOP:
+ {
+ $port = $next_port ;
+ $next_port = 2048 if ++$next_port > 65_535 ;
+ unless ( bind $listener, sockaddr_in( $port, INADDR_ANY ) ) {
+ push @errors, "$! on port $port" ;
+ croak join "\n", @errors if @errors > 10 ;
+ goto PORT_FINDER_LOOP;
+ }
+ }
+
+ _debug "win32 port = $port" if _debugging_details;
+
+ listen $listener, my $queue_size = 1
+ or croak "$!: listen()" ;
+
+ {
+ socket $client, PF_INET, SOCK_STREAM, $tcp_proto
+ or croak "$!: socket()";
+
+ my $paddr = sockaddr_in($port, $loopback );
+
+ connect $client, $paddr
+ or croak "$!: connect()" ;
+
+ croak "$!: accept" unless defined $paddr ;
+
+ ## The windows "default" is SO_DONTLINGER, which should make
+ ## sure all socket data goes through. I have my doubts based
+ ## on experimentation, but nothing prompts me to set SO_LINGER
+ ## at this time...
+ setsockopt $client, IPPROTO_TCP, TCP_NODELAY, pack("l", 0)
+ or croak "$!: setsockopt()";
+ }
+
+ {
+ _debug "accept()ing on port $port" if _debugging_details;
+ my $paddr = accept( $server, $listener ) ;
+ croak "$!: accept()" unless defined $paddr ;
+ }
+
+ _debug
+ "win32 _socket = ( ", fileno $server, ", ", fileno $client, " ) on port $port"
+ if _debugging_details;
+ return ( $server, $client ) ;
+}
+
+
+sub _open_socket_pipe {
+ my IPC::Run::Win32IO $self = shift;
+ my ( $debug_fd, $parent_handle ) = @_ ;
+
+ my $is_send_to_child = $self->dir eq "<";
+
+ $self->{CHILD_HANDLE} = gensym;
+ $self->{PUMP_PIPE_HANDLE} = gensym;
+
+ (
+ $self->{PARENT_HANDLE},
+ $self->{PUMP_SOCKET_HANDLE}
+ ) = _socket $parent_handle ;
+
+ ## These binmodes seem to have no effect on Win2K, but just to be safe
+ ## I do them.
+ binmode $self->{PARENT_HANDLE} or die $!;
+ binmode $self->{PUMP_SOCKET_HANDLE} or die $!;
+
+_debug "PUMP_SOCKET_HANDLE = ", fileno $self->{PUMP_SOCKET_HANDLE}
+ if _debugging_details;
+##my $buf ;
+##$buf = "write on child end of " . fileno( $self->{WRITE_HANDLE} ) . "\n\n\n\n\n" ;
+##POSIX::write(fileno $self->{WRITE_HANDLE}, $buf, length $buf) or warn "$! in syswrite" ;
+##$buf = "write on parent end of " . fileno( $self->{CHILD_HANDLE} ) . "\r\n" ;
+##POSIX::write(fileno $self->{CHILD_HANDLE},$buf, length $buf) or warn "$! in syswrite" ;
+## $self->{CHILD_HANDLE}->autoflush( 1 ) ;
+## $self->{WRITE_HANDLE}->autoflush( 1 ) ;
+
+ ## Now fork off a data pump and arrange to return the correct fds.
+ if ( $is_send_to_child ) {
+ pipe $self->{CHILD_HANDLE}, $self->{PUMP_PIPE_HANDLE}
+ or croak "$! opening child pipe" ;
+_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
+ if _debugging_details;
+_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
+ if _debugging_details;
+ }
+ else {
+ pipe $self->{PUMP_PIPE_HANDLE}, $self->{CHILD_HANDLE}
+ or croak "$! opening child pipe" ;
+_debug "CHILD_HANDLE = ", fileno $self->{CHILD_HANDLE}
+ if _debugging_details;
+_debug "PUMP_PIPE_HANDLE = ", fileno $self->{PUMP_PIPE_HANDLE}
+ if _debugging_details;
+ }
+
+ ## These binmodes seem to have no effect on Win2K, but just to be safe
+ ## I do them.
+ binmode $self->{CHILD_HANDLE};
+ binmode $self->{PUMP_PIPE_HANDLE};
+
+ ## No child should ever see this.
+ _dont_inherit $self->{PARENT_HANDLE} ;
+
+ ## We clear the inherit flag so these file descriptors are not inherited.
+ ## It'll be dup()ed on to STDIN/STDOUT/STDERR before CreateProcess is
+ ## called and *that* fd will be inheritable.
+ _dont_inherit $self->{PUMP_SOCKET_HANDLE} ;
+ _dont_inherit $self->{PUMP_PIPE_HANDLE} ;
+ _dont_inherit $self->{CHILD_HANDLE} ;
+
+ ## Need to return $self so the HANDLEs don't get freed.
+ ## Return $self, $parent_fd, $child_fd
+ my ( $parent_fd, $child_fd ) = (
+ fileno $self->{PARENT_HANDLE},
+ fileno $self->{CHILD_HANDLE}
+ ) ;
+
+ ## Both PUMP_..._HANDLEs will be closed, no need to worry about
+ ## inheritance.
+ _debug "binmode on" if _debugging_data && $self->binmode;
+ _spawn_pumper(
+ $is_send_to_child
+ ? ( $self->{PUMP_SOCKET_HANDLE}, $self->{PUMP_PIPE_HANDLE} )
+ : ( $self->{PUMP_PIPE_HANDLE}, $self->{PUMP_SOCKET_HANDLE} ),
+ $debug_fd,
+ $self->binmode,
+ $child_fd . $self->dir . "pump" . $self->dir . $parent_fd,
+ ) ;
+
+{
+my $foo ;
+confess "PARENT_HANDLE no longer open"
+ unless POSIX::read( $parent_fd, $foo, 0 ) ;
+}
+
+ _debug "win32_fake_pipe = ( $parent_fd, $child_fd )"
+ if _debugging_details;
+
+ $self->{FD} = $parent_fd;
+ $self->{TFD} = $child_fd;
+}
+
+sub _do_open {
+ my IPC::Run::Win32IO $self = shift;
+
+ if ( $self->{SEND_THROUGH_TEMP_FILE} ) {
+ return $self->_send_through_temp_file( @_ );
+ }
+ elsif ( $self->{RECV_THROUGH_TEMP_FILE} ) {
+ return $self->_init_recv_through_temp_file( @_ );
+ }
+ else {
+ return $self->_open_socket_pipe( @_ );
+ }
+}
+
+=head1 AUTHOR
+
+Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
+
+=head1 COPYRIGHT
+
+Copyright 2001, Barrie Slaymaker, All Rights Reserved.
+
+You may use this under the terms of either the GPL 2.0 ir the Artistic License.
+
+=cut
+
+1;
--- /dev/null
+package IPC::Run::Win32Pump;
+
+=head1 NAME
+
+IPC::Run::Win32Pump - helper processes to shovel data to/from parent, child
+
+=head1 SYNOPSIS
+
+Internal use only; see IPC::Run::Win32IO and best of luck to you.
+
+=head1 DESCRIPTION
+
+See L<IPC::Run::Win32Helper|IPC::Run::Win32Helper> for details. This
+module is used in subprocesses that are spawned to shovel data to/from
+parent processes from/to their child processes. Where possible, pumps
+are optimized away.
+
+NOTE: This is not a real module: it's a script in module form, designed
+to be run like
+
+ $^X -MIPC::Run::Win32Pumper -e 1 ...
+
+It parses a bunch of command line parameters from IPC::Run::Win32IO.
+
+=cut
+
+use strict ;
+
+use Win32API::File qw(
+ OsFHandleOpen
+) ;
+
+
+my ( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label );
+BEGIN {
+ ( $stdin_fh, $stdout_fh, $debug_fh, $binmode, $parent_pid, $parent_start_time, $debug, $child_label ) = @ARGV ;
+ ## Rather than letting IPC::Run::Debug export all-0 constants
+ ## when not debugging, we do it manually in order to not even
+ ## load IPC::Run::Debug.
+ if ( $debug ) {
+ eval "use IPC::Run::Debug qw( :default _debug_init ); 1;"
+ or die $@;
+ }
+ else {
+ eval <<STUBS_END or die $@;
+ sub _debug {}
+ sub _debug_init {}
+ sub _debugging() { 0 }
+ sub _debugging_data() { 0 }
+ sub _debugging_details() { 0 }
+ sub _debugging_gory_details() { 0 }
+ 1;
+STUBS_END
+ }
+}
+
+## For some reason these get created with binmode on. AAargh, gotta #### REMOVE
+## do it by hand below. #### REMOVE
+if ( $debug ) { #### REMOVE
+close STDERR; #### REMOVE
+OsFHandleOpen( \*STDERR, $debug_fh, "w" ) #### REMOVE
+ or print "$! opening STDERR as Win32 handle $debug_fh in pumper $$" ; #### REMOVE
+} #### REMOVE
+close STDIN; #### REMOVE
+OsFHandleOpen( \*STDIN, $stdin_fh, "r" ) #### REMOVE
+or die "$! opening STDIN as Win32 handle $stdin_fh in pumper $$" ; #### REMOVE
+close STDOUT; #### REMOVE
+OsFHandleOpen( \*STDOUT, $stdout_fh, "w" ) #### REMOVE
+or die "$! opening STDOUT as Win32 handle $stdout_fh in pumper $$" ; #### REMOVE
+
+binmode STDIN;
+binmode STDOUT;
+$| = 1 ;
+select STDERR ; $| = 1 ; select STDOUT ;
+
+$child_label ||= "pump" ;
+_debug_init(
+$parent_pid,
+$parent_start_time,
+$debug,
+fileno STDERR,
+$child_label,
+) ;
+
+_debug "Entered" if _debugging_details ;
+
+# No need to close all fds; win32 doesn't seem to pass any on to us.
+$| = 1 ;
+my $buf ;
+my $total_count = 0 ;
+while (1) {
+my $count = sysread STDIN, $buf, 10_000 ;
+last unless $count ;
+if ( _debugging_gory_details ) {
+ my $msg = "'$buf'" ;
+ substr( $msg, 100, -1 ) = '...' if length $msg > 100 ;
+ $msg =~ s/\n/\\n/g ;
+ $msg =~ s/\r/\\r/g ;
+ $msg =~ s/\t/\\t/g ;
+ $msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg ;
+ _debug sprintf( "%5d chars revc: ", $count ), $msg ;
+}
+$total_count += $count ;
+$buf =~ s/\r//g unless $binmode;
+if ( _debugging_gory_details ) {
+ my $msg = "'$buf'" ;
+ substr( $msg, 100, -1 ) = '...' if length $msg > 100 ;
+ $msg =~ s/\n/\\n/g ;
+ $msg =~ s/\r/\\r/g ;
+ $msg =~ s/\t/\\t/g ;
+ $msg =~ s/([\000-\037\177-\277])/sprintf "\0x%02x", ord $1/eg ;
+ _debug sprintf( "%5d chars sent: ", $count ), $msg ;
+}
+print $buf ;
+}
+
+_debug "Exiting, transferred $total_count chars" if _debugging_details ;
+
+## Perform a graceful socket shutdown. Windows defaults to SO_DONTLINGER,
+## which should cause a "graceful shutdown in the background" on sockets.
+## but that's only true if the process closes the socket manually, it
+## seems; if the process exits and lets the OS clean up, the OS is not
+## so kind. STDOUT is not always a socket, of course, but it won't hurt
+## to close a pipe and may even help. With a closed source OS, who
+## can tell?
+##
+## In any case, this close() is one of the main reasons we have helper
+## processes; if the OS closed socket fds gracefully when an app exits,
+## we'd just redirect the client directly to what is now the pump end
+## of the socket. As it is, however, we need to let the client play with
+## pipes, which don't have the abort-on-app-exit behavior, and then
+## adapt to the sockets in the helper processes to allow the parent to
+## select.
+##
+## Possible alternatives / improvements:
+##
+## 1) use helper threads instead of processes. I don't trust perl's threads
+## as of 5.005 or 5.6 enough (which may be myopic of me).
+##
+## 2) figure out if/how to get at WaitForMultipleObjects() with pipe
+## handles. May be able to take the Win32 handle and pass it to
+## Win32::Event::wait_any, dunno.
+##
+## 3) Use Inline::C or a hand-tooled XS module to do helper threads.
+## This would be faster than #1, but would require a ppm distro.
+##
+close STDOUT ;
+close STDERR ;
+
+=head1 AUTHOR
+
+Barries Slaymaker <barries@slaysys.com>. Funded by Perforce Software, Inc.
+
+=head1 COPYRIGHT
+
+Copyright 2001, Barrie Slaymaker, All Rights Reserved.
+
+You may use this under the terms of either the GPL 2.0 ir the Artistic License.
+
+=cut
+
+1 ;
--- /dev/null
+# $Id: File.pm,v 1.1 2007/01/16 23:01:44 gumpu Exp $
+package Test::File;
+use strict;
+
+use base qw(Exporter);
+use vars qw(@EXPORT $VERSION);
+
+use File::Spec;
+use Test::Builder;
+
+@EXPORT = qw(
+ file_exists_ok file_not_exists_ok
+ file_empty_ok file_not_empty_ok file_size_ok file_max_size_ok
+ file_min_size_ok file_readable_ok file_not_readable_ok file_writeable_ok
+ file_not_writeable_ok file_executable_ok file_not_executable_ok
+ file_mode_is file_mode_isnt
+ file_is_symlink_ok
+ symlink_target_exists_ok
+ symlink_target_dangles_ok
+ link_count_is_ok link_count_gt_ok link_count_lt_ok
+ owner_is owner_isnt
+ group_is group_isnt
+ );
+
+$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+
+my $Test = Test::Builder->new();
+
+=head1 NAME
+
+Test::File -- test file attributes
+
+=head1 SYNOPSIS
+
+use Test::File;
+
+=head1 DESCRIPTION
+
+This modules provides a collection of test utilities for file
+attributes.
+
+Some file attributes depend on the owner of the process testing the
+file in the same way the file test operators do. For instance, root
+(or super-user or Administrator) may always be able to read files no
+matter the permissions.
+
+Some attributes don't make sense outside of Unix, either, so some
+tests automatically skip if they think they won't work on the
+platform. If you have a way to make these functions work on Windows,
+for instance, please send me a patch. :)
+
+=head2 Functions
+
+=cut
+
+sub _normalize
+ {
+ my $file = shift;
+ return unless defined $file;
+
+ return $file =~ m|/|
+ ? File::Spec->catfile( split m|/|, $file )
+ : $file;
+ }
+
+sub _win32
+ {
+ return 0 if $^O eq 'darwin';
+ return $^O =~ m/Win/;
+ }
+
+sub _no_symlinks_here { ! eval { symlink("",""); 1 } }
+
+# owner_is and owner_isn't should skip on OS where the question makes no
+# sence. I really don't know a good way to test for that, so I'm going
+# to skip on the two OS's that I KNOW aren't multi-user. I'd love to add
+# more if anyone knows of any
+# Note: I don't have a dos or mac os < 10 machine to test this on
+sub _obviously_non_multi_user
+ {
+ ($^O eq 'dos') ?
+ return 1
+ :
+ ($^O eq 'MacOS') ?
+ return 1
+ :
+ return;
+
+ eval { my $holder = getpwuid(0) };
+ return 1 if $@;
+
+ eval { my $holder = getgrgid(0) };
+ return 1 if $@;
+
+ return 0;
+ }
+
+=over 4
+
+=item file_exists_ok( FILENAME [, NAME ] )
+
+Ok if the file exists, and not ok otherwise.
+
+=cut
+
+sub file_exists_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename exists";
+
+ my $ok = -e $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag("File [$filename] does not exist");
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_not_exists_ok( FILENAME [, NAME ] )
+
+Ok if the file does not exist, and not okay if it does exist.
+
+=cut
+
+sub file_not_exists_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename does not exist";
+
+ my $ok = not -e $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag("File [$filename] exists");
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_empty_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and has empty size, not ok if the
+file does not exist or exists with non-zero size.
+
+=cut
+
+sub file_empty_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is empty";
+
+ my $ok = -z $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ if( -e $filename )
+ {
+ my $size = -s $filename;
+ $Test->diag( "File exists with non-zero size [$size] b");
+ }
+ else
+ {
+ $Test->diag( 'File does not exist');
+ }
+
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_not_empty_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and has non-zero size, not ok if the
+file does not exist or exists with zero size.
+
+=cut
+
+sub file_not_empty_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is not empty";
+
+ my $ok = not -z $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ if( -e $filename and -z $filename )
+ {
+ $Test->diag( "File [$filename] exists with zero size" );
+ }
+ else
+ {
+ $Test->diag( "File [$filename] does not exist" );
+ }
+
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_size_ok( FILENAME, SIZE [, NAME ] )
+
+Ok if the file exists and has SIZE size in bytes (exactly), not ok if
+the file does not exist or exists with size other than SIZE.
+
+=cut
+
+sub file_size_ok($$;$)
+ {
+ my $filename = _normalize( shift );
+ my $expected = int shift;
+ my $name = shift || "$filename has right size";
+
+ my $ok = ( -s $filename ) == $expected;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ unless( -e $filename )
+ {
+ $Test->diag( "File [$filename] does not exist" );
+ }
+ else
+ {
+ my $actual = -s $filename;
+ $Test->diag(
+ "File [$filename] has actual size [$actual] not [$expected]" );
+ }
+
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_max_size_ok( FILENAME, MAX [, NAME ] )
+
+Ok if the file exists and has size less than or equal to MAX bytes, not
+ok if the file does not exist or exists with size greater than MAX
+bytes.
+
+=cut
+
+sub file_max_size_ok($$;$)
+ {
+ my $filename = _normalize( shift );
+ my $max = int shift;
+ my $name = shift || "$filename is under $max bytes";
+
+ my $ok = ( -s $filename ) <= $max;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ unless( -e $filename )
+ {
+ $Test->diag( "File [$filename] does not exist" );
+ }
+ else
+ {
+ my $actual = -s $filename;
+ $Test->diag(
+ "File [$filename] has actual size [$actual] " .
+ "greater than [$max]"
+ );
+ }
+
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_min_size_ok( FILENAME, MIN [, NAME ] )
+
+Ok if the file exists and has size greater than or equal to MIN bytes,
+not ok if the file does not exist or exists with size less than MIN
+bytes.
+
+=cut
+
+sub file_min_size_ok($$;$)
+ {
+ my $filename = _normalize( shift );
+ my $min = int shift;
+ my $name = shift || "$filename is over $min bytes";
+
+ my $ok = ( -s $filename ) >= $min;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ unless( -e $filename )
+ {
+ $Test->diag( "File [$filename] does not exist" );
+ }
+ else
+ {
+ my $actual = -s $filename;
+ $Test->diag(
+ "File [$filename] has actual size ".
+ "[$actual] less than [$min]"
+ );
+ }
+
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_readable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is readable, not ok
+if the file does not exist or is not readable.
+
+=cut
+
+sub file_readable_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is readable";
+
+ my $ok = -r $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag( "File [$filename] is not readable" );
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_not_readable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is not readable, not ok
+if the file does not exist or is readable.
+
+=cut
+
+sub file_not_readable_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is not readable";
+
+ my $ok = not -r $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag( "File [$filename] is readable" );
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_writeable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is writeable, not ok
+if the file does not exist or is not writeable.
+
+=cut
+
+sub file_writeable_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is writeable";
+
+ my $ok = -w $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag( "File [$filename] is not writeable" );
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_not_writeable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is not writeable, not ok
+if the file does not exist or is writeable.
+
+=cut
+
+sub file_not_writeable_ok($;$)
+ {
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is not writeable";
+
+ my $ok = not -w $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag("File [$filename] is writeable");
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_executable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is executable, not ok
+if the file does not exist or is not executable.
+
+This test automatically skips if it thinks it is on a
+Windows platform.
+
+=cut
+
+sub file_executable_ok($;$)
+ {
+ if( _win32() )
+ {
+ $Test->skip( "file_executable_ok doesn't work on Windows" );
+ return;
+ }
+
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is executable";
+
+ my $ok = -x $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag("File [$filename] is not executable");
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_not_executable_ok( FILENAME [, NAME ] )
+
+Ok if the file exists and is not executable, not ok
+if the file does not exist or is executable.
+
+This test automatically skips if it thinks it is on a
+Windows platform.
+
+=cut
+
+sub file_not_executable_ok($;$)
+ {
+ if( _win32() )
+ {
+ $Test->skip( "file_not_executable_ok doesn't work on Windows" );
+ return;
+ }
+
+ my $filename = _normalize( shift );
+ my $name = shift || "$filename is not executable";
+
+ my $ok = not -x $filename;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag("File [$filename] is executable");
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_mode_is( FILENAME, MODE [, NAME ] )
+
+Ok if the file exists and the mode matches, not ok
+if the file does not exist or the mode does not match.
+
+This test automatically skips if it thinks it is on a
+Windows platform.
+
+Contributed by Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >>
+
+=cut
+
+sub file_mode_is($$;$)
+ {
+ if( _win32() )
+ {
+ $Test->skip( "file_mode_is doesn't work on Windows" );
+ return;
+ }
+
+ my $filename = _normalize( shift );
+ my $mode = shift;
+
+ my $name = shift || sprintf("%s mode is %04o", $filename, $mode);
+
+ my $ok = -e $filename && ((stat($filename))[2] & 07777) == $mode;
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag(sprintf("File [%s] mode is not %04o", $filename, $mode) );
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_mode_isnt( FILENAME, MODE [, NAME ] )
+
+Ok if the file exists and mode does not match, not ok
+if the file does not exist or mode does match.
+
+This test automatically skips if it thinks it is on a
+Windows platform.
+
+Contributed by Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >>
+
+=cut
+
+sub file_mode_isnt($$;$)
+ {
+ if( _win32() )
+ {
+ $Test->skip( "file_mode_isnt doesn't work on Windows" );
+ return;
+ }
+
+ my $filename = _normalize( shift );
+ my $mode = shift;
+
+ my $name = shift || sprintf("%s mode is not %04o",$filename,$mode);
+
+ my $ok = not (-e $filename && ((stat($filename))[2] & 07777) == $mode);
+
+ if( $ok )
+ {
+ $Test->ok(1, $name);
+ }
+ else
+ {
+ $Test->diag(sprintf("File [%s] mode is %04o",$filename,$mode));
+ $Test->ok(0, $name);
+ }
+ }
+
+=item file_is_symlink_ok( FILENAME [, NAME] )
+
+Ok is FILENAME is a symlink, even if it points to a non-existent
+file. This test automatically skips if the operating system does
+not support symlinks. If the file does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+=cut
+
+sub file_is_symlink_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "file_is_symlink_ok doesn't work on systems without symlinks" );
+ return;
+ }
+
+ my $file = shift;
+ my $name = shift || "$file is a symlink";
+
+ if( -l $file )
+ {
+ $Test->ok(1, $name)
+ }
+ else
+ {
+ $Test->diag( "File [$file] is not a symlink!" );
+ $Test->ok(0, $name);
+ }
+ }
+
+=item symlink_target_exists_ok( SYMLINK [, TARGET] [, NAME] )
+
+Ok is FILENAME is a symlink and it points to a existing file. With the
+optional TARGET argument, the test fails if SYMLINK's target is not
+TARGET. This test automatically skips if the operating system does not
+support symlinks. If the file does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+=cut
+
+sub symlink_target_exists_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "symlink_target_exists_ok doesn't work on systems without symlinks"
+ );
+ return;
+ }
+
+ my $file = shift;
+ my $dest = shift || readlink( $file );
+ my $name = shift || "$file is a symlink";
+
+ unless( -l $file )
+ {
+ $Test->diag( "File [$file] is not a symlink!" );
+ return $Test->ok( 0, $name );
+ }
+
+ unless( -e $dest )
+ {
+ $Test->diag( "Symlink [$file] points to non-existent target [$dest]!" );
+ return $Test->ok( 0, $name );
+ }
+
+ my $actual = readlink( $file );
+ unless( $dest eq $actual )
+ {
+ $Test->diag(
+ "Symlink [$file] points to\n\t$actual\nexpected\n\t$dest\n\n" );
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->ok( 1, $name );
+ }
+
+=item symlink_target_dangles_ok( SYMLINK [, NAME] )
+
+Ok if FILENAME is a symlink and if it doesn't point to a existing
+file. This test automatically skips if the operating system does not
+support symlinks. If the file does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+=cut
+
+sub symlink_target_dangles_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "symlink_target_exists_ok doesn't work on systems without symlinks" );
+ return;
+ }
+
+ my $file = shift;
+ my $dest = readlink( $file );
+ my $name = shift || "$file is a symlink";
+
+ unless( -l $file )
+ {
+ $Test->diag( "File [$file] is not a symlink!" );
+ return $Test->ok( 0, $name );
+ }
+
+ if( -e $dest )
+ {
+ $Test->diag(
+ "Symlink [$file] points to existing file [$dest] but shouldn't!" );
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->ok( 1, $name );
+ }
+
+=item link_count_is_ok( FILE, LINK_COUNT [, NAME] )
+
+Ok if the link count to FILE is LINK_COUNT. LINK_COUNT is interpreted
+as an integer. A LINK_COUNT that evaluates to 0 returns Ok if the file
+does not exist. This test automatically skips if the operating system
+does not support symlinks. If the file does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+
+=cut
+
+sub link_count_is_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "link_count_is_ok doesn't work on systems without symlinks" );
+ return;
+ }
+
+ my $file = shift;
+ my $count = int( 0 + shift );
+
+ my $name = shift || "$file has a link count of [$count]";
+
+ my $actual = (stat $file )[3];
+
+ unless( $actual == $count )
+ {
+ $Test->diag(
+ "File [$file] points has [$actual] links: expected [$count]!" );
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->ok( 1, $name );
+ }
+
+=item link_count_gt_ok( FILE, LINK_COUNT [, NAME] )
+
+Ok if the link count to FILE is greater than LINK_COUNT. LINK_COUNT is
+interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
+if the file has at least one link. This test automatically skips if
+the operating system does not support symlinks. If the file does not
+exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+=cut
+
+sub link_count_gt_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "link_count_gt_ok doesn't work on systems without symlinks" );
+ return;
+ }
+
+ my $file = shift;
+ my $count = int( 0 + shift );
+
+ my $name = shift || "$file has a link count of [$count]";
+
+ my $actual = (stat $file )[3];
+
+ unless( $actual > $count )
+ {
+ $Test->diag(
+ "File [$file] points has [$actual] links: ".
+ "expected more than [$count]!" );
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->ok( 1, $name );
+ }
+
+=item link_count_lt_ok( FILE, LINK_COUNT [, NAME] )
+
+Ok if the link count to FILE is less than LINK_COUNT. LINK_COUNT is
+interpreted as an integer. A LINK_COUNT that evaluates to 0 returns Ok
+if the file has at least one link. This test automatically skips if
+the operating system does not support symlinks. If the file does not
+exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+=cut
+
+sub link_count_lt_ok
+ {
+ if( _no_symlinks_here() )
+ {
+ $Test->skip(
+ "link_count_lt_ok doesn't work on systems without symlinks" );
+ return;
+ }
+
+ my $file = shift;
+ my $count = int( 0 + shift );
+
+ my $name = shift || "$file has a link count of [$count]";
+
+ my $actual = (stat $file )[3];
+
+ unless( $actual < $count )
+ {
+ $Test->diag(
+ "File [$file] points has [$actual] links: ".
+ "expected more than [$count]!" );
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->ok( 1, $name );
+ }
+
+
+# owner_is, owner_isnt, group_is and group_isnt are almost
+# identical in the beginning, so I'm writing a skeleton they can all use.
+# I can't think of a better name...
+sub _dm_skeleton
+ {
+ if( _obviously_non_multi_user() )
+ {
+ my $calling_sub = (caller(1))[3];
+ $Test->skip( $calling_sub . " only works on a multi-user OS" );
+ return 'skip';
+ }
+
+ my $filename = _normalize( shift );
+ my $testing_for = shift;
+ my $name = shift;
+
+ unless( defined $filename )
+ {
+ $Test->diag( "File name not specified" );
+ return $Test->ok( 0, $name );
+ }
+
+ unless( -e $filename )
+ {
+ $Test->diag( "File [$filename] does not exist" );
+ return $Test->ok( 0, $name );
+ }
+
+ return;
+ }
+
+=item owner_is( FILE , OWNER [, NAME] )
+
+Ok if FILE's owner is the same as OWNER. OWNER may be a text user name
+or a numeric userid. Test skips on Dos, and Mac OS <= 9.
+If the file does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+Contributed by Dylan Martin
+
+=cut
+
+sub owner_is
+ {
+ my $filename = shift;
+ my $owner = shift;
+ my $name = shift || "$filename belongs to $owner";
+
+ my $err = _dm_skeleton( $filename, $owner, $name );
+ return if( defined( $err ) && $err eq 'skip' );
+ return $err if defined($err);
+
+ my $owner_uid = _get_uid( $owner );
+
+ my $file_uid = ( stat $filename )[4];
+
+ unless( defined $file_uid )
+ {
+ $Test->skip("stat failed to return owner uid for $filename");
+ return;
+ }
+
+ return $Test->ok( 1, $name ) if $file_uid == $owner_uid;
+
+ my $real_owner = ( getpwuid $file_uid )[0];
+ unless( defined $real_owner )
+ {
+ $Test->diag("File does not belong to $owner");
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->diag( "File [$filename] belongs to $real_owner ($file_uid), ".
+ "not $owner ($owner_uid)" );
+ return $Test->ok( 0, $name );
+ }
+
+=item owner_isnt( FILE, OWNER [, NAME] )
+
+Ok if FILE's owner is not the same as OWNER. OWNER may be a text user name
+or a numeric userid. Test skips on Dos and Mac OS <= 9. If the file
+does not exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+Contributed by Dylan Martin
+
+=cut
+
+sub owner_isnt
+ {
+ my $filename = shift;
+ my $owner = shift;
+ my $name = shift || "$filename belongs to $owner";
+
+ my $err = _dm_skeleton( $filename, $owner, $name );
+ return if( defined( $err ) && $err eq 'skip' );
+ return $err if defined($err);
+
+ my $owner_uid = _get_uid( $owner );
+ my $file_uid = ( stat $filename )[4];
+
+ return $Test->ok( 1, $name ) if $file_uid != $owner_uid;
+
+ $Test->diag( "File [$filename] belongs to $owner ($owner_uid)" );
+ return $Test->ok( 0, $name );
+ }
+
+=item group_is( FILE , GROUP [, NAME] )
+
+Ok if FILE's group is the same as GROUP. GROUP may be a text group name or
+a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
+systems that do not support getpwuid() and friends. If the file does not
+exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+Contributed by Dylan Martin
+
+=cut
+
+sub group_is
+ {
+ my $filename = shift;
+ my $group = shift;
+ my $name = ( shift || "$filename belongs to group $group" );
+
+ my $err = _dm_skeleton( $filename, $group, $name );
+ return if( defined( $err ) && $err eq 'skip' );
+ return $err if defined($err);
+
+ my $group_gid = _get_gid( $group );
+ my $file_gid = ( stat $filename )[5];
+
+ unless( defined $file_gid )
+ {
+ $Test->skip("stat failed to return group gid for $filename");
+ return;
+ }
+
+ return $Test->ok( 1, $name ) if $file_gid == $group_gid;
+
+ my $real_group = ( getgrgid $file_gid )[0];
+ unless( defined $real_group )
+ {
+ $Test->diag("File does not belong to $group");
+ return $Test->ok( 0, $name );
+ }
+
+ $Test->diag( "File [$filename] belongs to $real_group ($file_gid), ".
+ "not $group ($group_gid)" );
+
+ return $Test->ok( 0, $name );
+ }
+
+=item group_isnt( FILE , GROUP [, NAME] )
+
+Ok if FILE's group is not the same as GROUP. GROUP may be a text group name or
+a numeric group id. Test skips on Dos, Mac OS <= 9 and any other operating
+systems that do not support getpwuid() and friends. If the file does not
+exist, the test fails.
+
+The optional NAME parameter is the name of the test.
+
+Contributed by Dylan Martin
+
+=cut
+
+sub group_isnt
+ {
+ my $filename = shift;
+ my $group = shift;
+ my $name = shift || "$filename does not belong to group $group";
+
+ my $err = _dm_skeleton( $filename, $group, $name );
+ return if( defined( $err ) && $err eq 'skip' );
+ return $err if defined($err);
+
+ my $group_gid = _get_gid( $group );
+ my $file_gid = ( stat $filename )[5];
+
+ unless( defined $file_gid )
+ {
+ $Test->skip("stat failed to return group gid for $filename");
+ return;
+ }
+
+ return $Test->ok( 1, $name ) if $file_gid != $group_gid;
+
+ $Test->diag( "File [$filename] belongs to $group ($group_gid)" );
+ return $Test->ok( 0, $name );
+ }
+
+sub _get_uid
+ {
+ my $owner = shift;
+ my $owner_uid;
+
+ if ($owner =~ /^\d+/)
+ {
+ $owner_uid = $owner;
+ $owner = ( getpwuid $owner )[0];
+ }
+ else
+ {
+ $owner_uid = (getpwnam($owner))[2];
+ }
+
+ $owner_uid;
+ }
+
+sub _get_gid
+ {
+ my $group = shift;
+ my $group_uid;
+
+ if ($group =~ /^\d+/)
+ {
+ $group_uid = $group;
+ $group = ( getgrgid $group )[0];
+ }
+ else
+ {
+ $group_uid = (getgrnam($group))[2];
+ }
+
+ $group_uid;
+ }
+
+=back
+
+=head1 TO DO
+
+* check properties for other users (readable_by_root, for instance)
+
+* check times
+
+* check number of links to file
+
+* check path parts (directory, filename, extension)
+
+=head1 SEE ALSO
+
+L<Test::Builder>,
+L<Test::More>
+
+=head1 SOURCE AVAILABILITY
+
+This source is part of a SourceForge project which always has the
+latest sources in CVS, as well as all of the previous releases.
+
+ http://sourceforge.net/projects/brian-d-foy/
+
+If, for some reason, I disappear from the world, one of the other
+members of the project can shepherd this module appropriately.
+
+=head1 AUTHOR
+
+brian d foy, C<< <bdfoy@cpan.org> >>
+
+=head1 CREDITS
+
+Shawn Sorichetti C<< <ssoriche@coloredblocks.net> >> provided
+some functions.
+
+Tom Metro helped me figure out some Windows capabilities.
+
+Dylan Martin added C<owner_is> and C<owner_isnt>
+
+=head1 COPYRIGHT
+
+Copyright 2002-2007, brian d foy, All Rights Reserved
+
+=head1 LICENSE
+
+You may use, modify, and distribute this under the same terms
+as Perl itself.
+
+=cut
+
+"The quick brown fox jumped over the lazy dog";
--- /dev/null
+test:
+ prove -l
+
--- /dev/null
+#!perl
+
+#------------------------------------------------------------------------------
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+
+#****x* Basics/One file per header
+# FUNCTION
+# Test --multidoc with --one_file_per_header for html output. We
+# test this with one source file that contains three headers. These
+# should result in three documentation files. To make it nasty we
+# use some special header names.
+#
+# SOURCE
+
+# A dummy header to put into dummy source files.
+my $source = <<'EOF';
+/****f* Test/Test
+ * NAME
+ * Test foo bar
+ ******
+ */
+
+/****f* Test/foo, bar
+ * NAME
+ * Test foo bar
+ ******
+ */
+
+/****f* Test/aa::awaw
+ * NAME
+ * Test foo bar
+ ******
+ */
+
+EOF
+
+{
+ add_source( "test.c", $source );
+ my ( $out, $err ) = runrobo(qw(--src Src --doc Doc --one_file_per_header --multidoc --html));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ # There are three headers, so there should be three documentation files.
+ file_exists_ok( 'Doc/test_cTest2FTest.html', 'Documentation for Test/Test' );
+ file_exists_ok( 'Doc/test_cTest2Ffoo.html', 'Documentation for Test/foo' );
+ file_exists_ok( 'Doc/test_cTest2Faa3A3Aawaw.html', 'Documentation for Test/aa::awaw' );
+ # And a style sheet.
+ file_exists_ok( "Doc/robodoc.css", 'and a stylesheet' );
+ clean();
+}
+
+#*******
+
--- /dev/null
+#/usr/bin/perl -w
+#vi: spell ff=unix
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+
+
+#****h* ROBODoc System Tests/Pipes
+# FUNCTION
+# Test the usage of pipes.
+#
+#****
+
+
+my $pipe_source = <<'EOF';
+/****f* Test/Test
+ * NAME
+ * Only "pipes" which match selected output style are picked up.
+ * |html <CENTER>This will be included in <B>HTML</B> output.</CENTER>
+ *
+ * |latex \centerline{This will be included in \LaTeX output}
+ *
+ * Space is mandatory following the pipe marker. The following is not a
+ * valid pipe marker:
+ * |html<B>Moi!</B>
+ *
+ * You should see an equation on the following line:
+ * |html y = x^2 (sorry, plain HTML is not very powerful)
+ * |latex \centerline{$y = x^2$}
+ *
+ * How does this look like?
+ *
+ * Here comes a multi-line equation array:
+ * |latex \begin{eqnarray}
+ * |latex \frac{\partial u}{\partial \tau} & = & D_u {\nabla}^2 u +
+ * |latex \frac{1}{\epsilon}
+ * |latex \left ( \hat{u}-{\hat{u}}^2-f\, {v} \, \frac{\hat{u}-q}{\hat{u}+q}
+ * |latex \right ) \; , \label{diffspot:1} \\
+ * |latex \frac{\partial v}{\partial \tau} & = & \hat{u}-v \; ,
+ * |latex \label{diffspot:2} \\
+ * |latex \frac{\partial r}{\partial \tau} & = & D_r {\nabla}^2 r \; .
+ * |latex \label{diffspAot:3}
+ * |latex \end{eqnarray}
+ * |html <I>TODO: write this in html</I>
+ * Another paragraph
+ * SOURCE
+ */
+ test()
+
+ /******/
+
+EOF
+
+
+#****x* Pipes/latex pipes
+# FUNCTION
+# Test the use of latex pipes preformatted more.
+# |latex $2n < 8$
+# SOURCE
+{
+ add_source( "test.c", $pipe_source );
+ mkdocdir();
+ my ( $out, $err ) = runrobo(
+ qw(--src Src
+ --doc Doc/test
+ --nopre
+ --singledoc
+ --sections
+ --latex
+ ));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ file_exists_ok( "Doc/test.tex", 'there should be documentation' );
+ # TODO test content
+ clean();
+}
+#****
+
+
+
--- /dev/null
+#****h* ROBODoc/ROBODoc System Tests
+# FUNCTION
+# A set of perl scripts that test ROBODoc functionallity.
+# Each script contains one of more system tests. Each test
+# starts ROBODoc with a specific input and then asserts that
+# ROBODoc produces the correct output.
+#
+# The tests use the Perl unittest framework, that is the
+# modules:
+# * Test::More, See http://perldoc.perl.org/Test/More.html
+# * Test::File
+#
+# There is also a custom module ROBOTestFrame that contains a
+# set of useful functions that are common to all tests.
+#
+# You can run the tests with
+# prove -l
+#
+# Tests go into files with the extension .t. You can
+# run an individual set of tests with
+# prove <testfile>
+#
+# A 'prove' should always result in a 100% score. No test
+# should fail.
+#
+#*****
+#
+
--- /dev/null
+#!perl -w
+# vim: spell ff=unix
+#****h* ROBODoc System Tests/ROBODoc Configuration File
+# FUNCTION
+# Test stuff that can be specified in the robodoc.rc file.
+#*****
+
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use XML::Simple;
+use Data::Dumper;
+
+#****x* ROBODoc Configuration File/Custom Header Markers
+# FUNCTION
+# Test whether custum header markers can be specified.
+# SOURCE
+#
+
+{
+ my $source = <<'EOF';
+/ ****f* Test/test
+/ NAME
+/ Test
+/ FUNCTION
+/ Test1
+/ SOURCE
+/ ****
+
+/ ****F* Foo/foo
+/ NAME
+/ Foo
+/ FUNCTION
+/ Test2
+/ SOURCE
+/ ****
+
+EOF
+
+ my $rc_file = <<'EOF';
+header markers:
+ / ****
+remark markers:
+ /
+end markers:
+ / ****
+headertypes:
+ f functions 1
+ F Foos 2
+EOF
+
+ add_source( "test.c", $source );
+ add_source( "robodoc.rc", $rc_file );
+ my ($out, $err) = runrobo( qw(--src Src --doc Doc --multidoc
+ --test --rc Src/robodoc.rc ) );
+ is( $out, '', 'no output' );
+ is( $err, '', 'no error' );
+ my $documentation = XMLin( 'Doc/test_c.xml' );
+ my $header = $documentation->{'header'};
+# print Dumper( $documentation );
+ isnt ( $header->{'Foo/foo'}, undef, 'There is a header named Foo/foo' );
+ isnt ( $header->{'Test/test'}, undef, 'There is a header named Test/foo' );
+ clean();
+}
+
+#****
+
+#****x* ROBODoc Configuration File/line-endings.
+# FUNCTION
+# ROBODoc should not care about the kind of line-endings that
+# are used. Either cr/lf or cr, or even lf should work without
+# any problem. We test this with two .rc files that have
+# different kind of line-endings.
+# SOURCE
+#
+
+{
+ my $source = <<'EOF';
+/****f* Test/test
+ * FOO
+ * test
+ ******/
+EOF
+ # A rc file with 'unix' line-endings.
+ my $config = read_hexdump( 'TestData/robodoc_unix_rc.xxd' );
+ add_configuration( "test.rc", $config, 'binary' );
+ add_source( "test.rc", $source );
+ mkdocdir();
+ my ( $out, $err ) = runrobo(qw(
+ --src Src
+ --doc Doc/test
+ --rc Config/test.rc
+ --singledoc --test --nopre ));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ clean();
+
+ # A rc file with 'windows' line-endings.
+ $config = read_hexdump( 'TestData/robodoc_windows_rc.xxd' );
+ add_configuration( "test.rc", $config, 'binary' );
+ add_source( "test.rc", $source );
+ mkdocdir();
+ ( $out, $err ) = runrobo(qw(
+ --src Src
+ --doc Doc/test
+ --rc Config/test.rc
+ --singledoc --test --nopre ));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+ clean();
+}
+
+#******
+
--- /dev/null
+#!perl
+# vi: spell ff=unix
+#****h* ROBODoc System Tests/Wiki Formatting
+# FUNCTION
+# Tests that test the Wiki like formatting that ROBODoc supports.
+#*****
+
+
+#------------------------------------------------------------------------------
+use strict;
+use warnings;
+use ROBOTestFrame;
+use Test::More 'no_plan';
+use Test::File;
+use XML::Simple;
+use Data::Dumper;
+
+#****x* Wiki Formatting/Wiki Basics
+# FUNCTION
+# Test a simple header: contains three lists, some paragraphs,
+# and some source. All should be recognized.
+# SOURCE
+
+{
+ my $source = <<'EOF';
+/****f* Test/Test
+ * NAME
+ *
+ * Implements serializers for the following
+ * files:
+ * - DZB_ACG - SAP accounting file record.
+ * - DZB_RRP - regularoty reporting file record.
+ * - DZB_MVT - Exchange Position File Record.
+ *
+ * A test
+ *
+ * Implements the following
+ * functions:
+ * - S99304_SERIALIZE_DZB_ACG
+ * - S99304_SERIALIZE_DZB_ACG_TBL
+ * - S99304_SERIALIZE_DZB_MVT
+ * and the functions:
+ * - S99304_SERIALIZE_DZB_MVT_TBL
+ * - S99304_SERIALIZE_DZB_RRP
+ * - S99304_SERIALIZE_DZB_RRP_TBL
+ * SOURCE
+ */
+ test()
+
+ /******/
+
+EOF
+
+ add_source( "test.c", $source );
+ my ( $out, $err ) = runrobo(qw(--src Src --doc Doc --nopre --multidoc --test));
+ # expected results:
+ is( $out, '', 'No ouput' );
+ is( $err, '', '... and no error' );
+
+ my $documentation = XMLin( 'Doc/test_c.xml' );
+ my $header = $documentation->{'header'};
+ is ( $header->{'name'}, 'Test/Test', 'Header is named Test/Test' );
+ my $items = $header->{'item'};
+ ok ( exists( $items->{'NAME'} ), 'header has an item NAME' );
+ ok ( exists( $items->{'SOURCE'} ), 'header has an item SOURCE' );
+ my $body = $items->{'NAME'}->{'item_body'};
+
+ # There are paragraphs.
+ ok ( exists( $body->{'para'} ), 'item has paragraphs' );
+
+ # There are three lists.
+ is ( scalar( @{ $body->{'list'} } ), 3, 'item has three lists' );
+ clean();
+}
+
+#******
+
+
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/****h* ROBODoc/Test_Generator
+ * FUNCTION
+ * The generator for test output.
+ *
+ * The purpose of this generator is to create output that is easily
+ * scanable by the system test scripts. This to make it easier to
+ * write tests for ROBODoc.
+ *
+ * This generator produces output in utf-8 encoding.
+ *
+ * This generator is experimental.
+ *
+ *******
+ * $Id: test_generator.c,v 1.10 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "test_generator.h"
+#include "globals.h"
+#include "util.h"
+
+
+
+char *RB_TEST_Get_Default_Extension(
+ void )
+{
+ return ".xml";
+}
+
+void RB_TEST_Generate_String(
+ FILE *dest_doc,
+ char *a_string )
+{
+ int i;
+ int l = strlen( a_string );
+ unsigned char c;
+
+ for ( i = 0; i < l; ++i )
+ {
+ c = a_string[i];
+ RB_TEST_Generate_Char( dest_doc, c );
+ }
+}
+
+/* TODO Documentation */
+
+void RB_TEST_Generate_Label(
+ FILE *dest_doc,
+ char *name )
+{
+ int i;
+ int l = strlen( name );
+ unsigned char c;
+
+ fprintf( dest_doc, "<label>" );
+ for ( i = 0; i < l; ++i )
+ {
+ c = name[i];
+ if ( utf8_isalnum( c ) )
+ {
+ RB_TEST_Generate_Char( dest_doc, c );
+ }
+ else
+ {
+ char buf[4];
+
+ sprintf( buf, "%02x", c );
+ RB_TEST_Generate_Char( dest_doc, buf[0] );
+ RB_TEST_Generate_Char( dest_doc, buf[1] );
+ }
+ }
+ fprintf( dest_doc, "</label>" );
+}
+
+
+/****f* Generator/RB_TEST_Generate_Char
+ * NAME
+ * RB_TEST_Generate_Char
+ * SYNOPSIS
+ * void RB_TEST_Generate_Char( FILE * dest_doc, int c )
+ * FUNCTION
+ * Switchboard to RB_TEST_Generate_Char
+ * SOURCE
+ */
+
+void RB_TEST_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ switch ( c )
+ {
+ default:
+ RB_FputcLatin1ToUtf8( dest_doc, c );
+ break;
+ }
+}
+
+/*****/
+
+void RB_TEST_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ fprintf( dest_doc, "<header name=\"" );
+ RB_TEST_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "\" header_module=\"" );
+ RB_TEST_Generate_String( dest_doc, cur_header->module_name );
+ fprintf( dest_doc, "\"" );
+ fprintf( dest_doc, " header_function_name=\"" );
+ RB_TEST_Generate_String( dest_doc, cur_header->function_name );
+ fprintf( dest_doc, "\" >\n" );
+}
+
+void RB_TEST_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+
+ fprintf( dest_doc, "</header>\n" );
+}
+
+void RB_TEST_Generate_Link(
+ FILE *dest,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname
+ )
+{
+ /* TODO print the other stuff too! */
+ USE( dest_name );
+ USE( filename );
+
+ fprintf( dest, "<link labelname=\"%s\" linkname=\"", labelname );
+ RB_TEST_Generate_String( dest, linkname );
+ fprintf( dest, "\" />\n" );
+}
+
+void RB_TEST_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc )
+{
+ USE( toc );
+
+ if ( course_of_action.do_headless )
+ {
+ /* The user does not want the document head. */
+ }
+ else
+ {
+ fprintf( dest_doc, "<doc_start src_name=\"%s\" name=\"%s\">\n",
+ src_name, name );
+ }
+}
+
+
+void RB_TEST_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( name );
+
+ if ( course_of_action.do_footless )
+ {
+ /* The user does not want the foot of the
+ * document.
+ */
+ }
+ else
+ {
+ fprintf( dest_doc, "%s", "</doc_start>\n" );
+ }
+}
+
+
+void RB_TEST_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "<item id=\"" );
+ RB_TEST_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "\">\n" );
+}
+
+void RB_TEST_Generate_Item_Begin(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<item_body>\n" );
+}
+
+
+void RB_TEST_Generate_Item_End(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</item_body>\n</item>\n" );
+}
+
+
+void RB_TEST_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ fprintf( dest_doc, "<section depth=\"%d\">", depth );
+ RB_TEST_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "\n" );
+}
+
+void RB_TEST_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( depth );
+
+ fprintf( dest_doc, "</section>" );
+ RB_TEST_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "\n" );
+}
+
+
+void RB_TEST_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "<false_link>" );
+ RB_TEST_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "</false_link>" );
+}
+
+
+
+void TEST_Generate_Begin_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<para>\n" );
+}
+
+void TEST_Generate_End_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</para>\n" );
+}
+
+
+void TEST_Generate_Begin_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<pre>\n" );
+}
+
+void TEST_Generate_End_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</pre>\n" );
+}
+
+
+void TEST_Generate_Begin_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<list>\n" );
+}
+
+void TEST_Generate_End_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</list>\n" );
+}
+
+void TEST_Generate_Begin_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "<list_item>\n" );
+}
+
+void TEST_Generate_End_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "</list_item>\n" );
+}
--- /dev/null
+#ifndef ROBODOC_TEST_GENERATOR_H
+#define ROBODOC_TEST_GENERATOR_H
+
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+#include "document.h"
+
+#define MAX_SECTION_DEPTH 7
+
+void RB_TEST_Generate_Label(
+ FILE *dest_doc,
+ char *name );
+void RB_TEST_Generate_Item_Begin(
+ FILE *dest_doc );
+void RB_TEST_Generate_Item_End(
+ FILE *dest_doc );
+void RB_TEST_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count );
+void RB_TEST_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+void RB_TEST_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+char *RB_TEST_Get_Default_Extension(
+ void );
+void RB_TEST_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc );
+void RB_TEST_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+void RB_TEST_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_TEST_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_TEST_Generate_Index(
+ struct RB_Document *document,
+ FILE *dest,
+ char *dest_name );
+void RB_TEST_Generate_Index_Table(
+ FILE *dest,
+ char *dest_name,
+ int type,
+ char *title );
+void RB_TEST_Generate_Empty_Item(
+ FILE *dest );
+void RB_TEST_Generate_Link(
+ FILE *dest,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname );
+void RB_TEST_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_TEST_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+char *RB_TEST_RelativeAddress(
+ char *thisname,
+ char *thatname );
+int RB_TEST_Generate_Extra(
+ FILE *dest_doc,
+ int item_type,
+ char *cur_char );
+void RB_TEST_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+
+void TEST_Generate_Begin_Paragraph(
+ FILE *dest_doc );
+void TEST_Generate_End_Paragraph(
+ FILE *dest_doc );
+void TEST_Generate_Begin_Preformatted(
+ FILE *dest_doc );
+void TEST_Generate_End_Preformatted(
+ FILE *dest_doc );
+void TEST_Generate_Begin_List(
+ FILE *dest_doc );
+void TEST_Generate_End_List(
+ FILE *dest_doc );
+void TEST_Generate_Begin_List_Item(
+ FILE *dest_doc );
+void TEST_Generate_End_List_Item(
+ FILE *dest_doc );
+
+
+#endif /* ROBODOC_TEST_GENERATOR_H */
--- /dev/null
+/* obsolete */
--- /dev/null
+#!/usr/bin/perl -w
+#
+use strict;
+use IO::File;
+
+my $count = 0;
+
+sub scan_file {
+ my $file = shift;
+ my $sourcefile = IO::File->new("<$file") or
+ die "can't open $file";
+ my @source = <$sourcefile>;
+ my @todos;
+ my $key = "???";
+ foreach my $line (@source) {
+ if ($line =~ /TODO/i) {
+ push(@todos, $line);
+ }
+ }
+ if (scalar(@todos)) {
+ print "File: $file\n";
+ foreach my $line (@todos) {
+ if ($line =~ m/TODO(.*)$/i) {
+ print " TODO $1\n";
+ ++$count;
+ }
+ }
+ }
+}
+
+
+sub scan_directory {
+ my $file;
+ opendir(DIR, '.') or die;
+ while (defined($file = readdir(DIR))) {
+ if ($file =~ m/\.(c|h)$/i) {
+ scan_file($file);
+ }
+ }
+ closedir(DIR);
+}
+
+
+sub main {
+ if (@ARGV) {
+ foreach my $file (@ARGV) {
+ if ($file =~ m/\.(c|h)$/) {
+ scan_file $file;
+ }
+ }
+ } else {
+ scan_directory;
+ }
+ print $count, " TODOs to go\n";
+
+}
+
+main;
+
+
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+
+#if defined(__MINGW32__) || defined( RB_MSVC )
+
+#include "items.h"
+#include "headers.h"
+#include "robodoc.h"
+
+/* Set of dummy functions just to make it compile. */
+char *RB_TROFF_Get_Default_Extension(
+ void )
+{
+ return ".3"; /* Section 3 of manual */
+}
+
+void RB_TROFF_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( dest_doc );
+ USE( name );
+}
+
+FILE *RB_TROFF_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( dest_doc );
+ USE( cur_header );
+ return NULL;
+}
+
+void RB_TROFF_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( dest_doc );
+ USE( cur_header );
+}
+
+
+void RB_TROFF_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ USE( dest_doc );
+ USE( c );
+}
+
+void RB_TROFF_Generate_False_Link(
+ FILE *out,
+ char *name )
+{
+ USE( out );
+ USE( name );
+
+}
+
+void RB_TROFF_Generate_Item_Name(
+ FILE *out,
+ char *name,
+ int pre )
+{
+ USE( out );
+ USE( name );
+ USE( pre );
+}
+
+void RB_TROFF_Start_New_Line(
+ FILE *out )
+{
+ USE( out );
+}
+
+int RB_TROFF_Generate_Extra(
+ FILE *out,
+ enum ItemType item_type,
+ char *str )
+{
+ USE( out );
+ USE( item_type );
+ USE( str );
+ return -1;
+}
+
+void RB_TROFF_Set_Param(
+ char *compress,
+ char *section )
+{
+ USE( compress );
+ USE( section );
+}
+
+void TROFF_Generate_Begin_List_Item(
+ FILE *out )
+{
+ USE( out );
+}
+
+void TROFF_Generate_End_List_Item(
+ FILE *out )
+{
+ USE( out );
+}
+
+void TROFF_Generate_Begin_Preformatted(
+ FILE *out )
+{
+ USE( out );
+}
+
+void TROFF_Generate_End_Preformatted(
+ FILE *out )
+{
+ USE( out );
+}
+
+void TROFF_Generate_End_Paragraph(
+ FILE *out )
+{
+ USE( out );
+}
+
+void TROFF_Generate_Begin_Paragraph(
+ FILE *out )
+{
+ USE( out );
+}
+
+#else
+
+#include <sys/param.h>
+
+#include "troff_generator.h"
+#include "util.h"
+#include "robodoc.h"
+#include "items.h"
+#include "headers.h"
+#include "generator.h"
+#include "file.h"
+#include "part.h"
+
+static int skip_space = 1;
+static int in_list = 0;
+static int preformat = 0;
+static int skippre = 0;
+static int end_of_line = 0;
+static int end_of_para = 0;
+
+static char *compress_cmd = NULL;
+static char *compress_ext = NULL;
+static char *man_section = NULL;
+
+/****h* ROBODoc/TROFF_Generator
+ * FUNCTION
+ * Generator for TROFF output.
+ * NOTE
+ * Almost finished.
+ ******
+ */
+
+char *RB_TROFF_Get_Default_Extension(
+ void )
+{
+ return ".3"; /* Section 3 of manual */
+}
+
+/****f* TROFF_Generator/RB_TROFF_Generate_Doc_End
+ * NAME
+ * RB_TROFF_Generate_Doc_End --
+ *****
+ */
+
+void RB_TROFF_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+
+}
+
+static FILE *open_output_file(
+ char *out )
+{
+ if ( compress_cmd )
+ {
+ FILE *file;
+ char *cmd =
+ malloc( strlen( out ) + 10 + strlen( compress_cmd ) );
+
+ sprintf( cmd, "%s > %s", compress_cmd, out );
+
+ file = popen( cmd, "w" );
+ free( cmd );
+ return file;
+ }
+ else
+ return fopen( out, "w" );
+}
+
+static void write_comment(
+ FILE *out,
+ char *str )
+{
+ int start = 1;
+
+ while ( *str )
+ {
+ if ( start )
+ {
+ fputs( ".\\\" ", out );
+ start = 0;
+ }
+ switch ( *str )
+ {
+ case '.':
+ fputs( "\\.", out );
+ break;
+ case '-':
+ fputs( "\\-", out );
+ break;
+ case '\\':
+ fputs( "\\\\", out );
+ break;
+ case '\n':
+ start = 1; /* no break; */
+ default:
+ fputc( *str, out );
+ }
+ str++;
+ }
+ if ( start == 0 )
+ fputc( '\n', out );
+}
+
+static char *basename(
+ char *str )
+{
+ char *base = strrchr( str, '/' );
+
+ return base ? base + 1 : str;
+}
+
+/****f* TROFF_Generator/RB_TROFF_Generate_Header_Start
+ * NAME
+ * RB_TROFF_Generate_Header_Start
+ ******
+ */
+
+FILE *RB_TROFF_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ static char *manpage = NULL;
+
+ if ( manpage == NULL )
+ manpage = malloc( MAXPATHLEN );
+
+ if ( cur_header->name )
+ {
+ char *file =
+ RB_Get_FullDocname( cur_header->owner->filename );
+ char *name = basename( cur_header->name );
+ char *path = basename( file );
+ int len, i;
+
+ len = path - file;
+ memcpy( manpage, file, len );
+ sprintf( manpage + len, "%s.%s%s", name, man_section, compress_ext );
+
+ dest_doc = open_output_file( manpage );
+ if ( dest_doc == NULL )
+ {
+ RB_Panic( "Cannot open %s: %s\n", manpage, strerror( errno ) );
+ }
+
+ RB_Say( "+ Generating man page \"%s\"\n", SAY_INFO, manpage );
+
+ /* Check for aliases */
+ for ( i = 1; i < cur_header->no_names; i++ )
+ {
+ char *base = basename( cur_header->names[i] );
+ char *buf = strchr( manpage, 0 ) + 1;
+
+ memcpy( buf, file, len );
+ sprintf( buf + len, "%s.%s%s", base, man_section, compress_ext );
+ unlink( buf );
+ symlink( basename( manpage ), buf );
+ RB_Say( "+ Linked with \"%s\"\n", SAY_INFO, buf );
+ }
+
+ /* Append document type and title */
+ fprintf( dest_doc, ".\\\" Source: %s\n",
+ cur_header->owner->filename->fullname );
+
+ write_comment( dest_doc, COMMENT_ROBODOC COMMENT_COPYRIGHT );
+
+ fprintf( dest_doc, ".TH %s %s \"", name, man_section );
+ {
+ time_t ttp;
+ char timeBuffer[255];
+
+ time( &ttp );
+ strftime( timeBuffer, sizeof timeBuffer, "%b %d, %Y",
+ localtime( &ttp ) );
+ fputs( timeBuffer, dest_doc );
+
+ if ( name > cur_header->name )
+ {
+ path = cur_header->name;
+ len = name - path - 1;
+ }
+ else
+ len = strlen( path = name );
+ if ( len >= sizeof timeBuffer )
+ len = sizeof timeBuffer - 1;
+ memcpy( timeBuffer, path, len );
+ timeBuffer[len] = 0;
+ fprintf( dest_doc, "\" %s \"%s Reference\"\n", timeBuffer,
+ timeBuffer );
+ }
+ }
+ return dest_doc;
+}
+
+
+/****f* TROFF_Generator/RB_TROFF_Generate_Header_End
+ * NAME
+ * RB_TROFF_Generate_Header_End --
+ *****
+ */
+void RB_TROFF_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ fclose( dest_doc );
+}
+
+
+void RB_TROFF_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ if ( skip_space && utf8_isspace( c ) )
+ return;
+ skip_space = 0;
+ if ( in_list == 1 && utf8_isspace( c ) )
+ {
+ fprintf( dest_doc, "\n\\-\\ " );
+ in_list = 2;
+ skip_space = 1;
+ return;
+ }
+ else
+ switch ( c )
+ {
+ case '\n':
+ assert( 0 );
+ break;
+ case '\t':
+ assert( 0 );
+ break;
+ case '\\':
+ case '-':
+ case '.':
+ fputc( '\\', dest_doc );
+ fputc( c, dest_doc );
+ break;
+ default:
+ fputc( c, dest_doc );
+ }
+ end_of_para = 0;
+}
+
+void RB_TROFF_Generate_False_Link(
+ FILE *out,
+ char *name )
+{
+ fprintf( out, "\\fB%s\\fR", name );
+ if ( in_list )
+ fprintf( out, " \\- " );
+ in_list = 0;
+}
+
+void RB_TROFF_Generate_Item_Name(
+ FILE *out,
+ char *name,
+ int pre )
+{
+ if ( preformat )
+ {
+ fprintf( out, "\n.fi" );
+ }
+ fprintf( out, "\n.SH %s\n", name );
+ if ( ( preformat = pre ) )
+ {
+ fprintf( out, ".nf\n" );
+ skippre = -1;
+ }
+ else
+ skippre = 0;
+ in_list = 0;
+}
+
+void RB_TROFF_Start_New_Line(
+ FILE *out )
+{
+ if ( end_of_para && in_list )
+ {
+ fprintf( out, "\n.PP" );
+ in_list = 0;
+ }
+ skip_space = 1;
+ end_of_line = 1;
+ end_of_para = 1;
+}
+
+int RB_TROFF_Generate_Extra(
+ FILE *out,
+ enum ItemType item_type,
+ char *str )
+{
+ if ( end_of_line == 0 )
+ return -1;
+ end_of_line = 0;
+ if ( preformat )
+ {
+ int len = strlen( str );
+
+ if ( skippre == -1 )
+ {
+ for ( skippre = 0; utf8_isspace( *str ); skippre++, str++ );
+ if ( skippre == 0 )
+ skippre--;
+ }
+
+ len = skippre > len ? len : skippre;
+ skip_space = 0;
+ return len > 0 ? len - 1 : -1;
+ }
+ else
+ {
+ char *p = str;
+ char *skip;
+ char *item;
+
+ /* Start of a line */
+ while ( utf8_isspace( *p ) )
+ p++;
+ skip = p;
+ if ( strchr( "-o*", *p ) == NULL )
+ {
+ while ( !utf8_isspace( *p ) && *p )
+ p++;
+ item = p;
+ while ( utf8_isspace( *p ) )
+ p++;
+ if ( *p != '-' )
+ return -1;
+ p++;
+ if ( *p == '-' )
+ p++;
+ if ( !utf8_isspace( p[1] ) )
+ return -1;
+ in_list = 1;
+ *item = 0;
+ fprintf( out, ".TP\n.I %s\n\\-\\ ", skip );
+ strcpy( p, p + 2 );
+ skip = item;
+ *item = ' ';
+ }
+ else
+ {
+ if ( !utf8_isspace( p[1] ) )
+ return -1;
+ fprintf( out, ".IP -\n" );
+ skip++;
+ }
+ in_list = 2;
+ skip_space = 1;
+ return skip > str ? skip - str - 1 : -1;
+ }
+ return -1;
+}
+
+void RB_TROFF_Set_Param(
+ char *compress,
+ char *section )
+{
+ if ( compress == NULL || *compress == 0
+ || strcasecmp( compress, "none" ) == 0 )
+ {
+ compress_cmd = NULL;
+ compress_ext = "";
+ }
+ else if ( strcasecmp( compress, "gzip" ) == 0 )
+ {
+ compress_cmd = "gzip -c";
+ compress_ext = ".gz";
+ }
+ else if ( strcasecmp( compress, "bzip2" ) == 0 )
+ {
+ compress_cmd = "bzip2 -c";
+ compress_ext = ".bz2";
+ }
+ else
+ {
+ RB_Panic( "Unknown compression compress \"%s\"\n", compress );
+ }
+ if ( section == NULL || *section == 0 )
+ man_section = "3";
+ else
+ man_section = section;
+}
+
+void TROFF_Generate_Begin_List_Item(
+ FILE *out )
+{
+/* fprintf( out, ".IP -\n" ); */
+}
+
+void TROFF_Generate_End_List_Item(
+ FILE *out )
+{
+/* fprintf( out, ".PP\n" ); */
+}
+
+void TROFF_Generate_Begin_Preformatted(
+ FILE *out )
+{
+/* fprintf( out, ".nf\n" ); */
+}
+
+void TROFF_Generate_End_Preformatted(
+ FILE *out )
+{
+/* fprintf( out, ".fi\n" ); */
+}
+
+void TROFF_Generate_End_Paragraph(
+ FILE *out )
+{
+/* fputc( '\n', out ); */
+}
+
+void TROFF_Generate_Begin_Paragraph(
+ FILE *out )
+{
+/* fprintf( out, ".PP\n" ); */
+}
+
+#endif
+
--- /dev/null
+#ifndef ROBODOC_TROFF_GENERATOR_H
+#define ROBODOC_TROFF_GENERATOR_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+#include "items.h"
+
+char *RB_TROFF_Get_Default_Extension(
+ void );
+void RB_TROFF_Generate_Doc_Start(
+ FILE *dest_doc,
+ char *src_name,
+ char *name,
+ char toc );
+void RB_TROFF_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+FILE *RB_TROFF_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_TROFF_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_TROFF_Generate_Empty_Item(
+ FILE *dest );
+void RB_TROFF_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_TROFF_Generate_False_Link(
+ FILE *out,
+ char *name );
+void RB_TROFF_Generate_Item_Name(
+ FILE *out,
+ char *name,
+ int pre );
+int RB_TROFF_Generate_Extra(
+ FILE *out,
+ enum ItemType item_type,
+ char *str );
+void TROFF_Generate_Begin_List_Item(
+ FILE *out );
+void TROFF_Generate_End_List_Item(
+ FILE *out );
+void TROFF_Generate_Begin_Preformatted(
+ FILE *out );
+void TROFF_Generate_End_Preformatted(
+ FILE *out );
+void TROFF_Generate_End_Paragraph(
+ FILE *out );
+void TROFF_Generate_Begin_Paragraph(
+ FILE *out );
+void RB_TROFF_Set_Param(
+ char *compress,
+ char *section );
+void RB_TROFF_Start_New_Line(
+ FILE *out );
+
+
+#endif /* ROBODOC_TROFF_GENERATOR_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <assert.h>
+#include <stdio.h>
+#include "document.h"
+#include "file.h"
+#include "path.h"
+#include "part.h"
+#include "directory.h"
+#include "globals.h"
+#include "robodoc.h"
+#include "generator.h"
+#include "analyser.h"
+
+int test_document( );
+int test_directory( );
+int test_file( );
+
+/*
+ The output of this programmed if diffed with a reference
+ file to see if robodoc still does what is supposed to do.
+*/
+
+int
+main( int argc, char **argv )
+{
+ course_of_action = course_of_action | DO_TELL;
+ whoami = argv[0];
+ printf( "robodoc unittest\n" );
+ test_file( );
+ test_directory( );
+ test_document( );
+ return EXIT_SUCCESS;
+}
+
+
+/*
+ Test struct RB_Document and it's functions.
+*/
+int
+test_document( )
+{
+ struct RB_Document *document = NULL;
+ struct RB_Filename *rbfilename = NULL;
+ struct RB_Path *rbpath = NULL;
+ struct RB_Part *rbpart = NULL;
+
+ /* Try creating and deleting and empty document */ .
+ document = RB_Get_RB_Document( );
+ assert( document );
+ RB_Free_RB_Document( document );
+
+
+ /* Now we fill it with some stuff. */
+ document = RB_Get_RB_Document( );
+ assert( document );
+ rbpath = RB_Get_RB_Path( "/home/robodoc/test2/" );
+ rbfilename = RB_Get_RB_Filename( "robodoc.c", rbpath );
+
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rbfilename ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rbfilename ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rbfilename ) );
+
+ rbpart = RB_Get_RB_Part( );
+ RB_Part_Add_Source( rbpart, rbfilename );
+
+ rbfilename = RB_Get_RB_Filename( "analyser.c", rbpath );
+ RB_Part_Add_Source( rbpart, rbfilename );
+
+ RB_Part_Dump( rbpart );
+
+ RB_Document_Add_Part( document, rbpart );
+
+ RB_Analyse_Document_NG( document );
+
+ RB_Generate_Documentation_NG( document );
+
+ RB_Free_RB_Document( document );
+
+ return 0;
+}
+
+int
+test_directory( )
+{
+ struct RB_Directory *rb_directory = NULL;
+
+ printf( "test_directory\n" );
+
+ rb_directory = RB_Get_RB_Directory( "/home/robodoc/test2/" );
+ assert( rb_directory );
+ RB_Dump_RB_Directory( rb_directory );
+ RB_Free_RB_Directory( rb_directory );
+ return 0;
+}
+
+
+/* Test path and file functions. */
+
+int
+test_file( )
+{
+ struct RB_Path *rb_path1 = NULL;
+ struct RB_Filename *rb_filename1 = NULL;
+
+ printf( "test_file\n" );
+ rb_path1 = RB_Get_RB_Path( "/home/robodoc/test2/" );
+ rb_filename1 = RB_Get_RB_Filename( "test.c", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ rb_path1 = RB_Get_RB_Path( "/home/robodoc/test2/" );
+ assert( rb_path1 );
+ rb_filename1 = RB_Get_RB_Filename( "Makefile", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ rb_path1 = RB_Get_RB_Path( "./" );
+ assert( rb_path1 );
+ rb_filename1 = RB_Get_RB_Filename( "test.cpp", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ rb_path1 = RB_Get_RB_Path( "./" );
+ assert( rb_path1 );
+ rb_filename1 = RB_Get_RB_Filename( ".testrc", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ /* Try something with a lot of dots */
+ rb_path1 = RB_Get_RB_Path( "./../../" );
+ assert( rb_path1 );
+ rb_filename1 = RB_Get_RB_Filename( "file.c.doc", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ /* Lets see if a slash gets added if we don't
+ specify the end slash in a path name. */
+ rb_path1 = RB_Get_RB_Path( "/home/robodoc/test2" );
+ assert( rb_path1 );
+ rb_filename1 = RB_Get_RB_Filename( "Makefile", rb_path1 );
+ assert( rb_filename1 );
+ printf( "Fullname [ %s ]\n", RB_Get_Fullname( rb_filename1 ) );
+ printf( "Path [ %s ]\n", RB_Get_Path( rb_filename1 ) );
+ printf( "Extension [ %s ]\n", RB_Get_Extension( rb_filename1 ) );
+
+ RB_Free_RB_Filename( rb_filename1 );
+
+ return 0;
+}
--- /dev/null
+#ifndef ROBODOC_UNITTEST_H
+#define ROBODOC_UNITTEST_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+
+
+#endif /* ROBODOC_UNITTEST_H */
--- /dev/null
+// vi: ff=unix spell
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+/****h* ROBODoc/Utilities
+ * FUNCTION
+ * Set of general purpose utility functions that are used
+ * in more than one module.
+ *****
+ * $Id: util.c,v 1.57 2007/07/10 19:13:52 gumpu Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h> /* for RB_Say() */
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <sys/stat.h>
+
+#if defined (RB_MSVC)
+/* empty */
+#else
+#include <unistd.h>
+#endif
+
+#include "robodoc.h"
+#include "globals.h"
+#include "links.h"
+#include "headers.h"
+#include "path.h"
+#include "util.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+
+static void RB_Swap(
+ void **array,
+ int left,
+ int right );
+
+
+/*===============================================================================*/
+
+
+/****f* Utilities/ExpandTab
+ * FUNCTION
+ * Expand the tabs in a line of text.
+ * SYNOPSIS
+ */
+
+char *ExpandTab(
+ char *line )
+/*
+ * INPUTS
+ * line -- the line to be expanded
+ * tab_size -- global.
+ * RETURN
+ * pointer to the expanded line.
+ * NOTE
+ * This function is not reentrant.
+ * SOURCE
+ */
+
+{
+ char *cur_char = line;
+ int n = 0;
+ int jump = 0;
+ char *newLine = NULL;
+ int lineBufLen = 1;
+ int actual_tab = 0;
+
+ lineBufLen = strlen( line ) + 1;
+
+ if ( ( newLine = malloc( lineBufLen * sizeof( char ) ) ) == NULL )
+ {
+ RB_Panic( "Out of memory! ExpandTab()\n" );
+ }
+
+ for ( ; *cur_char; ++cur_char )
+ {
+ if ( *cur_char == '\t' )
+ {
+ int i;
+
+ // Seek to actual tab stop position in tabstop table
+ while ( ( tab_stops[actual_tab] <= n )
+ && ( actual_tab < ( MAX_TABS - 1 ) ) )
+ {
+ actual_tab++;
+ }
+
+ jump = tab_stops[actual_tab] - n;
+
+ // If jump gets somehow negative fix it...
+ if ( jump < 0 )
+ {
+ jump = 1;
+ }
+
+ lineBufLen += jump;
+ if ( ( newLine = realloc( newLine, sizeof( char ) * lineBufLen ) )
+ == NULL )
+ {
+ RB_Panic( "Out of memory! ExpandTab()\n" );
+ }
+ for ( i = 0; i < jump; i++ )
+ {
+ newLine[n] = ' ';
+ ++n;
+ }
+ }
+ else
+ {
+ newLine[n] = *cur_char;
+ ++n;
+ }
+ }
+ newLine[n] = '\0';
+
+ return newLine;
+}
+
+/******/
+
+
+/****f* Utilities/RB_Alloc_Header
+ * FUNCTION
+ * allocate the struct RB_header
+ * SYNOPSIS
+ */
+
+struct RB_header *RB_Alloc_Header(
+ void )
+/*
+ * RESULT
+ * struct RB_header * -- all attributes/pointers set to zero
+ * AUTHOR
+ * Koessi
+ * SEE ALSO
+ * RB_Free_Header()
+ * SOURCE
+ */
+
+{
+ struct RB_header *new_header;
+
+ if ( ( new_header = malloc( sizeof( struct RB_header ) ) ) != NULL )
+ {
+ memset( new_header, 0, sizeof( struct RB_header ) );
+ }
+ else
+ {
+ RB_Panic( "out of memory! [Alloc Header]\n" );
+ }
+ return ( new_header );
+}
+
+/********/
+
+
+/****f* Utilities/RB_Free_Header
+ * NAME
+ * RB_Free_Header -- oop
+ * SYNOPSIS
+ */
+void RB_Free_Header(
+ struct RB_header *header )
+/*
+ * FUNCTION
+ * free struct RB_header and associated strings
+ * INPUTS
+ * struct RB_header *header -- this one
+ * AUTHOR
+ * Koessi
+ * SEE ALSO
+ * RB_Alloc_Header(), RB_Close_The_Shop()
+ * SOURCE
+ */
+
+{
+ if ( header )
+ {
+ if ( header->function_name )
+ {
+ free( header->function_name );
+ }
+ if ( header->version )
+ {
+ free( header->version );
+ }
+ if ( header->name )
+ {
+ free( header->name );
+ }
+ if ( header->unique_name )
+ {
+ free( header->unique_name );
+ }
+ if ( header->lines )
+ {
+ int i;
+
+ for ( i = 0; i < header->no_lines; ++i )
+ {
+ free( header->lines[i] );
+ }
+ free( header->lines );
+ }
+ free( header );
+ }
+}
+
+/************/
+
+
+/****if* Utilities/RB_StrDup
+ * NAME
+ * RB_StrDup
+ * SYNOPSIS
+ */
+
+char *RB_StrDup(
+ char *str )
+/*
+ * FUNCTION
+ * duplicate the given string.
+ * INPUTS
+ * char *str -- source
+ * RESULT
+ * char * -- destination
+ * AUTHOR
+ * Koessi
+ * SOURCE
+ */
+{
+ char *dupstr;
+ if ( ( dupstr =
+ malloc( ( strlen( str ) + 1 ) * sizeof( char ) ) ) != NULL )
+ {
+ strcpy( dupstr, str );
+ }
+ else
+ {
+ RB_Panic( "out of memory! [StrDup]\n" );
+ }
+ return ( dupstr );
+}
+
+
+char *RB_StrDupLen(
+ char *str,
+ size_t length )
+{
+ char *new = malloc( length + 1 );
+
+ if ( new )
+ {
+ memcpy( new, str, length );
+ new[length] = 0;
+ }
+ else
+ {
+ RB_Panic( "out of memory! [StrDupLen]\n" );
+ }
+ return new;
+}
+
+
+/*** RB_StrDup ***/
+
+/****f* Utilities/RB_Say [2.01]
+ * NAME
+ * RB_Say -- varargs
+ * SYNOPSIS
+ */
+void RB_Say(
+ char *format,
+ long mode,
+ ... )
+/*
+ * FUNCTION
+ * Say what's going on. Goes to stdout.
+ * INPUTS
+ * * char *format -- formatstring
+ * * long mode -- SAY_INFO | SAY_DEBUG
+ * * ... -- parameters
+ * AUTHOR
+ * Koessi
+ * HISTORY
+ * 22. Nov. 2005 - Multiple mode support (Michel Albert)
+ * SOURCE
+ */
+
+{
+ va_list ap;
+
+ if ( course_of_action.do_tell && debugmode & mode )
+ {
+ va_start( ap, mode );
+ printf( "%s: ", whoami );
+ vprintf( format, ap );
+ va_end( ap );
+ }
+}
+
+/*** RB_Say ***/
+
+
+/*x**f* Analyser/RB_SetCurrentFile
+ * NAME
+ * RB_SetCurrentFile
+ * FUNCTION
+ * Set... (TODO Documentation)
+ * INPUTS
+ * * filename --
+ * SOURCE
+ */
+
+void RB_SetCurrentFile(
+ char *filename )
+{
+ current_file = filename;
+}
+
+/*******/
+
+
+/****f* Analyser/RB_GetCurrentFile
+ * NAME
+ * Get a copy of the name of the current file.
+ * Allocates memory.
+ * SOURCE
+ */
+
+char *RB_GetCurrentFile(
+ void )
+{
+ if ( current_file )
+ {
+ return RB_StrDup( current_file );
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/*******/
+
+
+
+/****f* Utilities/Path2Win32Path
+ * SUMMARY
+ * Convert a path to a path in MS windows format.
+ ****
+ */
+
+char *Path_2_Win32Path(
+ char *path )
+{
+ char *cur_char = path;
+
+ for ( ; *cur_char; ++cur_char )
+ {
+ if ( *cur_char )
+ {
+ if ( *cur_char == '/' )
+ {
+ *cur_char = '\\';
+ }
+ }
+ }
+ return path;
+}
+
+/****f* Utilities/RB_Panic [2.01]
+ * NAME
+ * RB_Panic -- free resources and shut down
+ * SYNOPSIS
+ */
+
+void RB_Panic(
+ char *format,
+ ... )
+/*
+ * FUNCTION
+ * Print error message. Frees all resources used by robodoc.
+ * Terminates program. Output goes to stderr
+ * INPUTS
+ * char *format -- formatstring
+ * ... -- parameters
+ * AUTHOR
+ * Koessi
+ * SOURCE
+ */
+
+{
+ va_list ap;
+ char *name;
+
+ va_start( ap, format );
+
+ name = RB_GetCurrentFile( );
+
+#if defined(RB_BCC)
+ if ( course_of_action.do_ms_errors )
+ {
+ Path_2_Win32Path( name );
+ }
+#endif
+
+ if ( name )
+ {
+ char *buffer_copy = RB_StrDup( myLine );
+
+ RB_StripCR( buffer_copy );
+ fprintf( stderr, "%s:\n%s(%d) : Error E1:\n", whoami, name,
+ line_number );
+ fprintf( stderr, " %s\n%s: ", whoami, buffer_copy );
+ free( buffer_copy );
+ free( name );
+ }
+ else
+ {
+ fprintf( stderr, "%s: ", whoami );
+ }
+ vfprintf( stderr, format, ap );
+ fprintf( stderr, "%s: closing down...\n", whoami );
+ va_end( ap );
+ RB_Close_The_Shop( );
+ exit( EXIT_FAILURE );
+}
+
+/*** RB_Panic ***/
+
+
+/****f* Analyser/RB_Warning_Full
+ * NAME
+ * RB_Warning_Full
+ * FUNCTION
+ * Print warning to stdout.
+ * INPUTS
+ * * arg_filename --
+ * * arg_line_number --
+ * * arg_format --
+ * * ...
+ * SOURCE
+ */
+
+void RB_Warning_Full(
+ char *arg_filename,
+ int arg_line_number,
+ char *arg_format,
+ ... )
+{
+ va_list ap;
+
+ ++number_of_warnings;
+ va_start( ap, arg_format );
+ fprintf( stderr, "%s: Warning - %s:%d\n", whoami, arg_filename,
+ arg_line_number );
+ fprintf( stderr, " " );
+ vfprintf( stderr, arg_format, ap );
+ va_end( ap );
+}
+
+/*******/
+
+
+/****f* Analyser/RB_Warning
+ * NAME
+ * RB_Warning
+ * FUNCTION
+ * Print warning to stdout. (stderr better?)
+ * INPUTS
+ * * format --
+ * * ... --
+ * SOURCE
+ */
+
+void RB_Warning(
+ char *format,
+ ... )
+{
+ static int count = 1;
+ va_list ap;
+ char *name;
+
+ ++number_of_warnings;
+ va_start( ap, format );
+
+ name = RB_GetCurrentFile( );
+
+#if defined (RB_BCC)
+ if ( course_of_action.do_ms_errors )
+ {
+ Path_2_Win32Path( name );
+ }
+#endif
+
+ if ( name )
+ {
+ fprintf( stderr, "%s:\n%s(%d) : Warning R%d:\n", whoami, name,
+ line_number, count );
+ free( name );
+ }
+
+ fprintf( stderr, " " );
+ vfprintf( stderr, format, ap );
+ va_end( ap );
+
+ ++count;
+}
+
+/*******/
+
+
+/****f* Utilities/RB_Str_Case_Cmp
+ * FUNCTION
+ * Compare two strings, regardless of the case of the characters.
+ * SYNOPSIS
+ */
+int RB_Str_Case_Cmp(
+ char *s,
+ char *t )
+/*
+ * RESULT
+ * 0 s == t
+ * -1 s < t
+ * 1 s > t
+ * SOURCE
+ */
+{
+ assert( s );
+ assert( t );
+ for ( ; tolower( *s ) == tolower( *t ); s++, t++ )
+ {
+ if ( *s == '\0' )
+ {
+ return 0;
+ }
+ }
+ return ( int ) ( tolower( *s ) - tolower( *t ) );
+}
+
+/*********/
+
+
+/****f* Utilities/RB_TimeStamp
+ * NAME
+ * RB_TimeStamp -- print a time stamp
+ * SOURCE
+ */
+
+void RB_TimeStamp(
+ FILE *f )
+{
+ time_t ttp;
+ char timeBuffer[255];
+
+ time( &ttp );
+ strftime( timeBuffer, 255, "%a %b %d %Y %H:%M:%S\n", localtime( &ttp ) );
+ fprintf( f, "%s", timeBuffer );
+}
+
+/******/
+
+
+/****f* Utilities/RB_Skip_Whitespace
+ * SYNOPSIS
+ * char * RB_Skip_Whitespace(char *buf)
+ * FUNCTION
+ * Skip space and tab chars from the start *buf. This is needed when
+ * searching for indented headers and items.
+ * NOTES
+ * We should extract some info about indentation level and save it to
+ * global variable in order to write out source items (that originate from
+ * indented headers) neatly.
+ * SEE ALSO
+ * RB_Find_Marker, RB_Find_End_Marker, RB_Find_Item, RB_Generate_Item_Body
+ * SOURCE
+ */
+
+char *RB_Skip_Whitespace(
+ char *buf )
+{
+ char *c;
+
+ for ( c = buf; *c; c++ )
+ {
+ if ( utf8_isspace( *c ) )
+ {
+
+ }
+ else
+ {
+ return c;
+ }
+ }
+ return c; /* buf was null */
+}
+
+/*** RB_Skip_Whitespace ***/
+
+
+/****f* Utilities/RB_FputcLatin1ToUtf8
+ * NAME
+ * RB_FputcLatin1ToUtf8
+ * SYNOPSIS
+ * void RB_FputcLatin1ToUtf8(FILE *fp, int c)
+ * BUGS
+ * This wrongly assumes that input is always Latin-1.
+ * SOURCE
+ */
+
+void RB_FputcLatin1ToUtf8(
+ FILE *fp,
+ int c )
+{
+ if ( c < 0x80 )
+ {
+ if ( fputc( c, fp ) == EOF )
+ RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
+ }
+ else
+ {
+ if ( fputc( ( 0xC0 | ( c >> 6 ) ), fp ) == EOF )
+ RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
+ if ( fputc( ( 0x80 | ( c & 0x3F ) ), fp ) == EOF )
+ RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
+ }
+}
+
+/*** RB_FputcLatin1ToUtf8 ***/
+
+
+/****f* Utilities/RB_CopyFile
+ * NAME
+ * RB_CopyFile -- copy a file to another file
+ * SYNOPSIS
+ * void RB_CopyFile( char* sourceFileName, char* destinationFileName )
+ * RESULT
+ * Program Exit if one of the specified files did not open.
+ * SOURCE
+ */
+
+void RB_CopyFile(
+ char *sourceFileName,
+ char *destinationFileName )
+{
+ FILE *source;
+
+ source = fopen( sourceFileName, "r" );
+ if ( source )
+ {
+ FILE *dest;
+
+ dest = fopen( destinationFileName, "w" );
+ if ( dest )
+ {
+ for ( ; fgets( line_buffer, MAX_LINE_LEN, source ); )
+ {
+ fputs( line_buffer, dest );
+ }
+ }
+ else
+ {
+ fclose( source );
+ RB_Panic( "Can't open file %s for writing.\n",
+ destinationFileName );
+ }
+ }
+ else
+ {
+ RB_Panic( "Can't open file %s for reading\n", sourceFileName );
+ }
+}
+
+/*****/
+
+
+/****f* Utilities/RB_Match
+ * FUNCTION
+ * See if a wildcard expression matches a target string. The wildcard
+ * expression can consists of any literal character and the two
+ * wildcards characters '*' and '?'. '*' matches the longest string
+ * of zero or more characters that fit. '?' matches any single
+ * character.
+ *
+ * Examples:
+ * "*aap" matches "aapaapaapaap"
+ * "?inux" matches "linux"
+ * "lin*ux" matches "linux"
+ * "linux*" matches "linux"
+ * NOTES
+ * This is a recursive function.
+ * SYNOPSIS
+ * int RB_Match( char* target, char* wildcard_expression )
+ * INPUTS
+ * o target -- the string to be matched agains the
+ * wildcard_expression.
+ * o wildcard_expression -- the wildcard expression
+ * RETURN VALUE
+ * TRUE -- the target matches the wildcard expression
+ * FALSE -- it does not match.
+ * SOURCE
+ */
+
+int RB_Match(
+ char *target,
+ char *wildcard_expression )
+{
+ if ( ( *wildcard_expression == '\0' ) && ( *target == '\0' ) )
+ {
+ /* a match, since both strings are now "" */
+ return TRUE;
+ }
+ else if ( *wildcard_expression == '\0' )
+ {
+ /* we reached the end of the wildcard_expression,
+ * but not the end of the target, this is not
+ * a match.
+ */
+ return FALSE;
+ }
+ else if ( *target == '\0' )
+ {
+ /* we reached the end of the target but not the end of the
+ * wildcard_expression. Only if the whole wildcard_expression
+ * consists of * we have a match.
+ */
+ unsigned int i;
+
+ for ( i = 0; i < strlen( wildcard_expression ); ++i )
+ {
+ if ( wildcard_expression[i] != '*' )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ else
+ {
+ /* There are wildcard_expression characters left
+ * and target characters left.
+ */
+ char wildcard = wildcard_expression[0];
+
+ if ( wildcard == '?' )
+ {
+ /* Match a single character and see if the
+ * rest of the target matches.
+ */
+ return RB_Match( target + 1, wildcard_expression + 1 );
+ }
+ else if ( wildcard == '*' )
+ {
+ int match = FALSE;
+ int l = strlen( target );
+ int i;
+
+ /* First try to match all of the target string, and
+ * then work back to the begin of the target string.
+ * ( including the "" string. )
+ */
+ for ( i = l; i >= 0; --i )
+ {
+ if ( RB_Match( target + i, wildcard_expression + 1 ) )
+ {
+ match = TRUE;
+ break;
+ }
+ }
+ return match;
+ }
+ else
+ {
+ int l_w = strlen( wildcard_expression );
+ int l_t = strlen( target );
+
+ /* The minimum of the length of the wildcard_expression
+ * and target expression
+ */
+ int l = ( l_w <= l_t ) ? l_w : l_t;
+ int i;
+
+ for ( i = 0; i < l; ++i )
+ {
+ if ( ( wildcard_expression[i] != '*' ) &&
+ ( wildcard_expression[i] != '?' ) )
+ {
+ /* Some OS-es are not case-sensitive when it comes
+ * to file names, and consider Readme to be equal
+ * to README. On these OS-es it can be handy if
+ * robodoc is also not case-sensitive.
+ */
+#ifdef IGNORE_CASE_FILENAMES
+ if ( tolower( wildcard_expression[i] ) !=
+ tolower( target[i] ) )
+#else
+ if ( wildcard_expression[i] != target[i] )
+#endif
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ return RB_Match( target + i, wildcard_expression + i );
+ }
+ }
+ /* The first l characters of the target and
+ * wildcard_expression matched, now see if the rest
+ * matches too.
+ */
+ return RB_Match( target + l, wildcard_expression + l );
+ }
+ }
+}
+
+/******/
+
+
+/****f* Utilities/RB_Swap
+ * FUNCTION
+ * Swap two elements in a array of pointers. This function is used
+ * by RB_QuickSort().
+ * SOURCE
+ */
+
+static void RB_Swap(
+ void **array,
+ int left,
+ int right )
+{
+ void *p = array[left];
+
+ array[left] = array[right];
+ array[right] = p;
+}
+
+/*****/
+
+
+/****f* Utilities/RB_QuickSort
+ * FUNCTION
+ * Sort an array of pointers according to the lexical order
+ * of the elements the pointers point to.
+ * This is based on the quicksort routine in
+ * "The C programming language" by B Kerninghan en D Ritchie.
+ * INPUTS
+ * * array -- the array of pointers.
+ * * left -- the most left element in the array.
+ * * right -- the most right element in the array.
+ * * f -- pointer to a function that can compare
+ * the objects two elements of the array
+ * point to.
+ * RESULT
+ * array -- A sorted array of pointers.
+ *
+ * EXAMPLE
+ * The following is an example program that shows
+ * the use
+ * #define TEST_SIZE 10
+ *
+ * char* test[ TEST_SIZE ] = { "ape", "zebra",
+ * "duck", "goofbal", "dodo", "rabit",
+ * "crow", "cow", "pig", "goat" };
+ *
+ * int string_compare( void* p1, void* p2 )
+ * {
+ * char *cp1 = p1;
+ * char *cp2 = p2;
+ * return strcmp( cp1, cp2 );
+ * }
+ *
+ * RB_QuickSort( test, 0, TEST_SIZE - 1, string_compare );
+ *
+ * SOURCE
+ */
+
+void RB_QuickSort(
+ void **array,
+ int left,
+ int right,
+ TCompare f )
+{
+ int i;
+ int last;
+
+ if ( left >= right )
+ {
+ return;
+ }
+
+ RB_Swap( array, left, ( left + right ) / 2 );
+ last = left;
+ for ( i = left + 1; i <= right; ++i )
+ {
+ if ( ( *f ) ( array[i], array[left] ) < 0 )
+ {
+ RB_Swap( array, ++last, i );
+ }
+ }
+ RB_Swap( array, left, last );
+ RB_QuickSort( array, left, last - 1, f );
+ RB_QuickSort( array, last + 1, right, f );
+}
+
+/*******/
+
+
+/****f* Utilities/RB_StripCR
+ * FUNCTION
+ * Strip carriage return (CR) from line.
+ * INPUTS
+ * * line -- line string to process
+ * SOURCE
+ */
+
+void RB_StripCR(
+ char *line )
+{
+ char *c;
+
+ for ( c = line; *c; ++c )
+ {
+ if ( *c == '\n' )
+ {
+ *c = '\0';
+ }
+ }
+}
+
+/*******/
+
+
+/****f* Utilities/RB_ContainsNL
+ * FUNCTION
+ * Check whether the provided line buffer contains
+ * a new line (NL) character.
+ * INPUTS
+ * * line -- line string to process
+ * RETURN VALUE
+ * * TRUE -- the line contains a NL character
+ * * FALSE -- the line does not contain a NL character
+ * SOURCE
+ */
+
+int RB_ContainsNL(
+ char *line )
+{
+ int found = 0;
+
+ for ( ; *line != '\0'; ++line )
+ {
+ if ( *line == '\n' )
+ {
+ found = 1;
+ }
+ }
+ return found;
+}
+
+/*******/
+
+
+/****f* Utilities/RB_FreeLineBuffer
+ * FUNCTION
+ * Free the dynamically allocated line-buffer
+ * INPUTS
+ * * works on the globals line_buffer, readChars, myLine
+ * SOURCE
+ */
+
+void RB_FreeLineBuffer(
+ )
+{
+ *line_buffer = '\0';
+ free( myLine );
+ myLine = NULL;
+ readChars = 0;
+}
+
+/*******/
+
+/****f* Utilities/CR_LF_Conversion
+ * FUNCTION
+ * Fix CR/LF problems.
+ *
+ * If ROBODoc reads a text file that was created on another OS
+ * line-endings might not be what ROBODoc expects of the current OS.
+ * This function tries to detect and fix this.
+ *
+ * INPUTS
+ * * line -- a line of text
+ * RETURN VALUE
+ * * number of characters that were removed.
+ * SOURCE
+ */
+
+static int CR_LF_Conversion( char* line )
+{
+ int n = strlen( line );
+
+ if ( ( n > 1 ) &&
+ ( ( ( line[ n - 2 ] == '\r' ) && ( line [ n - 1 ] == '\n' ) )
+ ||
+ ( ( line[ n - 2 ] == '\n' ) && ( line [ n - 1 ] == '\r' ) )
+ )
+ )
+ {
+ line[ n - 2 ] = '\n';
+ line[ n - 1 ] = '\0';
+ return 1;
+ } else {
+ /* No problem */
+ return 0;
+ }
+}
+
+/******/
+
+
+/****f* Utilities/RB_ReadWholeLine
+ * FUNCTION
+ * Read a line from the file using the provided buffer.
+ * INPUTS
+ * * file -- file to read from
+ * * buf -- buffer of length MAX_LINE_LEN to read chunks of the line to
+ * * arg_readChars -- reference to the variable to store the read characters in
+ * RETURN VALUE
+ * * returns a dynamically allocated buffer containing the complete line
+ * read
+ * NOTES
+ * If the line did not end in a new line (NL) character one is added.
+ * SOURCE
+ */
+
+char *RB_ReadWholeLine(
+ FILE *file,
+ char *buf,
+ int *arg_readChars )
+{
+ int foundNL = 0;
+ char *line = NULL;
+ int curLineLen = 1;
+ int chunkLen = 0;
+
+ clearerr( file );
+ while ( ( !feof( file ) ) && ( !foundNL ) )
+ {
+ *buf = '\0';
+ /* read next chunk */
+ fgets( buf, MAX_LINE_LEN, file );
+ if ( ferror( file ) )
+ {
+ /* an error occurred */
+ RB_Panic( "I/O error %d! RB_ReadWholeLine()", errno );
+ }
+ chunkLen = strlen( buf );
+ curLineLen += chunkLen;
+ /* make room for the chunk in our buffer */
+ if ( ( line = realloc( line, sizeof( char ) * curLineLen ) ) == NULL )
+ {
+ /* we run out of memory */
+ RB_Panic( "Out of memory! RB_ReadWholeLine()" );
+ }
+ /* append the chunk to our buffer */
+ strcpy( ( line + curLineLen - chunkLen - 1 ), buf );
+
+ if ( RB_ContainsNL( buf ) )
+ {
+ /* we are done - a line was read */
+ foundNL = 1;
+ }
+ }
+
+ if ( !foundNL )
+ {
+ /* last line has no NL - add one */
+ ++curLineLen;
+ if ( ( line = realloc( line, sizeof( char ) * curLineLen ) ) == NULL )
+ {
+ /* we run out of memory */
+ RB_Panic( "Out of memory! RB_ReadWholeLine()" );
+ }
+ line[curLineLen - 2] = '\n';
+ line[curLineLen - 1] = '\0';
+ }
+
+ /* This fixes any cr/lf problems. */
+ curLineLen -= CR_LF_Conversion( line );
+
+ *arg_readChars = curLineLen;
+ *buf = '\0';
+ return line;
+}
+
+/*******/
+
+
+/****f* Utilities/Stat_Path
+ * FUNCTION
+ * Check the given path against required type.
+ * d -- directory, f -- file, e -- exists
+ * RETURN VALUE
+ * TRUE if path is of the given type, otherwise FALSE.
+ * BUGS
+ * Should check if symbolic link points to a directory or to a file.
+ * SOURCE
+ */
+
+int Stat_Path(
+ char required,
+ char *path )
+{
+ struct stat st;
+ int res = FALSE;
+
+ if ( stat( path, &st ) < 0 )
+ {
+ if ( required == 'e' )
+ {
+ res = FALSE;
+ }
+ else
+ {
+ if ( ( strcmp( path, "./" ) == 0 ) && ( required == 'd' ) )
+ {
+ /* This fixes a bug in Mingw, where ./ can not be
+ stat-ed under windows2000 and above,
+ we just assume that ./ always exists. */
+ res = TRUE;
+ }
+ else
+ {
+ RB_Panic( "Stat_Path: can not stat '%s'\n", path );
+ }
+ }
+ }
+ else
+ {
+ switch ( ( ( st.st_mode ) & S_IFMT ) )
+ {
+ case S_IFDIR:
+ if ( ( required == 'd' ) || ( required == 'e' ) )
+ {
+ res = TRUE;
+ }
+ break;
+ case S_IFREG:
+ if ( ( required == 'f' ) || ( required == 'e' ) )
+ {
+ res = TRUE;
+ }
+ break;
+ /* TODO case S_IFLNK: chdir() */
+ default:
+ break;
+ } /* end switch */
+ }
+ return res;
+}
+
+/*******/
+
+
+/****f* Utilities/RB_malloc
+ * FUNCTION
+ * like malloc, but exit if malloc failed
+ * RETURN VALUE
+ * See malloc
+ * SOURCE
+ */
+
+void *RB_malloc(
+ size_t bytes )
+{
+ void *tmp;
+
+ tmp = malloc( bytes );
+
+ if ( tmp == NULL )
+ {
+ RB_Panic( "Unable to malloc %d bytes", bytes );
+ }
+
+ return tmp;
+}
+
+/*******/
+
+
+/****v* Utilities/RB_crc32_table
+ * FUNCTION
+ * CRC32 lookup table for crc32 functions
+ * SOURCE
+ */
+static unsigned long *RB_crc32_table;
+
+/*******/
+
+
+/****f* Utilities/Make_crc32_table
+ * FUNCTION
+ * Builds up crc32 lookup table for crc32 function.
+ * Call this before calling to RB_crc32()
+ * SOURCE
+ */
+#define CRC32_GENERATOR_POLYNOMIAL 0xEDB88320
+
+void Make_crc32_table(
+ void )
+{
+ unsigned int i, n, carry;
+ unsigned long tmp;
+
+ // Allocate space for CRC table
+ RB_crc32_table = RB_malloc( 256 * sizeof( unsigned long ) );
+
+ for ( i = 0; i < 256; i++ )
+ {
+ tmp = i;
+ for ( n = 0; n < 8; n++ )
+ {
+ carry = tmp & 1;
+ tmp >>= 1;
+ if ( carry )
+ {
+ tmp ^= CRC32_GENERATOR_POLYNOMIAL;
+ }
+ }
+ RB_crc32_table[i] = tmp;
+ }
+}
+
+/*******/
+
+
+/****f* Utilities/RB_crc32
+ * FUNCTION
+ * calcualtes crc32 value for a given buffer
+ * INPUTS
+ * - buf: input buffer to calculate
+ * - len: length of buffer
+ * - crc32: CRC32 init value
+ * RETURN VALUE
+ * CRC32 value
+ * SOURCE
+ */
+unsigned long RB_crc32(
+ unsigned char *buf,
+ unsigned int len,
+ unsigned long crc32 )
+{
+ unsigned char tmp;
+
+ crc32 ^= 0xffffffffL;
+
+ while ( len-- )
+ {
+ tmp = *( buf++ ) ^ crc32;
+ crc32 >>= 8;
+ crc32 ^= RB_crc32_table[tmp];
+ }
+
+ return crc32 ^= 0xffffffffL;
+}
+
+/*******/
+
+
+/****f* Utilities/cwd
+ * FUNCTION
+ * Holds current working directory
+ * SOURCE
+ */
+static char *cwd = NULL;
+
+/*******/
+
+/****f* Utilities/RB_Get_Saved_CWD
+ * FUNCTION
+ * Changes back to saved working directory and frees cwd string
+ * SOURCE
+ */
+char *RB_Get_Saved_CWD(
+ void )
+{
+ return cwd;
+}
+
+/*******/
+
+/****f* Utilities/RB_Change_Back_To_CWD
+ * FUNCTION
+ * Changes back to saved working directory and frees cwd string
+ * SOURCE
+ */
+void RB_Change_Back_To_CWD(
+ void )
+{
+ if ( cwd != NULL )
+ {
+ chdir( cwd );
+ free( cwd );
+ cwd = NULL;
+ }
+}
+
+/*******/
+
+/****f* Utilities/RB_Change_To_Docdir
+ * FUNCTION
+ * Saves current working directory and then changes to document dir
+ * SOURCE
+ */
+void RB_Change_To_Docdir(
+ struct RB_Document *document )
+{
+ char tmp[TEMP_BUF_SIZE];
+
+ // Just for sure
+ RB_Change_Back_To_CWD( );
+
+ // Save CWD
+ getcwd( tmp, sizeof( tmp ) );
+ cwd = RB_StrDup( tmp );
+
+ // Get the name of doc directory
+ // I think this is not the right way to do it. Anyone help plz?
+ if ( course_of_action.do_multidoc )
+ {
+ chdir( document->docroot->name );
+ }
+ else if ( course_of_action.do_singledoc )
+ {
+ int len = strrchr( document->singledoc_name, '/' )
+ - document->singledoc_name;
+ strncpy( tmp, document->singledoc_name, len );
+ tmp[len] = 0;
+ chdir( tmp );
+ }
+}
+
+/*******/
+
+
+/****f* Utilities/Open_Pipe
+ * FUNCTION
+ * Opens a pipe and returns its handler
+ * SOURCE
+ */
+FILE *Open_Pipe(
+ char *pipe_name )
+{
+ FILE *a_pipe;
+
+#if defined(RB_MSVC)
+ a_pipe = _popen( pipe_name, "w" );
+#else
+ a_pipe = popen( pipe_name, "w" );
+#endif
+
+ if ( a_pipe == NULL )
+ {
+ RB_Panic( "Unable to open pipe to '%s'", pipe_name );
+ }
+ return a_pipe;
+}
+
+/*******/
+
+
+/****f* Utilities/Close_Pipe
+ * FUNCTION
+ * Closes a given pipe
+ * SOURCE
+ */
+void Close_Pipe(
+ FILE *arg_pipe )
+{
+ if ( arg_pipe != NULL )
+ {
+#if defined(RB_MSVC)
+ _pclose( arg_pipe );
+#else
+ pclose( arg_pipe );
+#endif
+ }
+}
+
+/*******/
+
+
+/* TODO Move this to a separate file */
+/* TODO grow this into a whole set of utf8 routines. */
+/* TODO Documentation */
+
+int utf8_isalnum(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isalnum( arg_c ) );
+}
+
+int utf8_isalpha(
+ unsigned int arg_c )
+{
+ /* We assume here that all non ascii characters
+ * are alphabetic characters. */
+ return ( ( arg_c >= 128 ) ||
+ ( arg_c < 128 && isalpha( arg_c ) ) );
+}
+
+int utf8_iscntrl(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && iscntrl( arg_c ) );
+}
+
+int utf8_isdigit(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isdigit( arg_c ) );
+}
+
+int utf8_isgraph(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isgraph( arg_c ) );
+}
+
+int utf8_islower(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && islower( arg_c ) );
+}
+
+int utf8_isprint(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isprint( arg_c ) );
+}
+
+int utf8_ispunct(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && ispunct( arg_c ) );
+}
+
+int utf8_isspace(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isspace( arg_c ) );
+}
+
+int utf8_isupper(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isupper( arg_c ) );
+}
+
+int utf8_isxdigit(
+ unsigned int arg_c )
+{
+ return ( ( arg_c < 128 ) && isxdigit( arg_c ) );
+}
--- /dev/null
+#ifndef ROBODOC_UTIL_H
+#define ROBODOC_UTIL_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
+*/
+
+
+/* TODO all the functions that operate
+ on headers should be in headers.h */
+
+#include "headers.h"
+#include "document.h"
+
+typedef int (
+ *TCompare ) (
+ void *,
+ void * );
+
+
+void RB_SetCurrentFile(
+ char *filename );
+char *RB_GetCurrentFile(
+ );
+
+char *RB_FilePart(
+ char * );
+void RB_Analyse_Defaults_File(
+ void );
+
+void RB_Slow_Sort(
+ void );
+void RB_Reverse_List(
+ void );
+void RB_Insert_In_List(
+ struct RB_header **,
+ struct RB_header * );
+void RB_Remove_From_List(
+ struct RB_header **,
+ struct RB_header * );
+struct RB_header *RB_Alloc_Header(
+ void );
+void RB_Free_Header(
+ struct RB_header * );
+char *RB_StrDup(
+ char * );
+char *RB_StrDupLen(
+ char *str,
+ size_t length );
+char *RB_CookStr(
+ char * );
+void RB_Say(
+ char *,
+ long mode,
+ ... );
+void RB_Warning_Full(
+ char *arg_filename,
+ int arg_line_number,
+ char *arg_format,
+ ... );
+void RB_Warning(
+ char *format,
+ ... );
+void RB_Panic(
+ char *,
+ ... );
+int RB_Str_Case_Cmp(
+ char *s,
+ char *t );
+void RB_TimeStamp(
+ FILE *f );
+char *RB_Skip_Whitespace(
+ char *buf );
+
+void *RB_Calloc(
+ size_t count,
+ size_t size );
+void *RB_Malloc(
+ size_t size );
+void RB_Free(
+ void *p );
+void RB_FputcLatin1ToUtf8(
+ FILE *fp,
+ int c );
+void RB_CopyFile(
+ char *sourceFileName,
+ char *destinationFileName );
+int RB_Match(
+ char *target,
+ char *wildcard_expression );
+void RB_QuickSort(
+ void **array,
+ int left,
+ int right,
+ TCompare f );
+void RB_StripCR(
+ char *line );
+int Stat_Path(
+ char required,
+ char *path );
+
+char *ExpandTab(
+ char *line );
+
+int RB_ContainsNL(
+ char *line );
+char *RB_ReadWholeLine(
+ FILE *file,
+ char *buf,
+ int *arg_readChars );
+void RB_FreeLineBuffer(
+ );
+char *Path_2_Win32Path(
+ char *path );
+
+void *RB_malloc(
+ size_t bytes );
+void Make_crc32_table(
+ void );
+unsigned long RB_crc32(
+ unsigned char *buf,
+ unsigned int len,
+ unsigned long crc32 );
+
+char *RB_Get_Saved_CWD(
+ void );
+void RB_Change_To_Docdir(
+ struct RB_Document *document );
+void RB_Change_Back_To_CWD(
+ void );
+FILE *Open_Pipe(
+ char *pipe_name );
+void Close_Pipe(
+ FILE *arg_pipe );
+
+
+void _RB_Mem_Check(
+ void *ptr,
+ const char *fname,
+ const char *funcname,
+ int linenum );
+
+
+
+int utf8_isalnum(unsigned int arg_c);
+int utf8_isalpha(unsigned int arg_c);
+int utf8_iscntrl(unsigned int arg_c);
+int utf8_isdigit(unsigned int arg_c);
+int utf8_isgraph(unsigned int arg_c);
+int utf8_islower(unsigned int arg_c);
+int utf8_isprint(unsigned int arg_c);
+int utf8_ispunct(unsigned int arg_c);
+int utf8_isspace(unsigned int arg_c);
+int utf8_isxdigit(unsigned int arg_c);
+
+
+/****f* Utilities/RB_Mem_Check
+ * FUNCTION
+ * Check for memory allocation failures.
+ * SOURCE
+ */
+
+#define RB_Mem_Check(ptr) if (!ptr) RB_Panic("%s:%s:%d: Out of memory!\n", __FILE__, __FUNCTION__, __LINE__)
+
+/*******/
+
+#endif /* ROBODOC_UTIL_H */
--- /dev/null
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "xmldocbook_generator.h"
+#include "globals.h"
+#include "util.h"
+
+
+char *RB_XMLDB_Get_Default_Extension(
+ void )
+{
+ return ".xml";
+}
+
+void RB_XMLDB_Generate_String(
+ FILE *dest_doc,
+ char *a_string )
+{
+ int i;
+ int l = strlen( a_string );
+ unsigned char c;
+
+ for ( i = 0; i < l; ++i )
+ {
+ c = a_string[i];
+ RB_XMLDB_Generate_Char( dest_doc, c );
+ }
+}
+
+/* TODO Documentation */
+
+void RB_XMLDB_Generate_Label(
+ FILE *dest_doc,
+ char *name )
+{
+ int i;
+ int l = strlen( name );
+ unsigned char c;
+
+ fprintf( dest_doc, "<anchor id=\"" );
+ for ( i = 0; i < l; ++i )
+ {
+ c = name[i];
+ if ( utf8_isalnum( c ) )
+ {
+ RB_XMLDB_Generate_Char( dest_doc, c );
+ }
+ else
+ {
+ char buf[4];
+
+ sprintf( buf, "%02x", c );
+ RB_XMLDB_Generate_Char( dest_doc, buf[0] );
+ RB_XMLDB_Generate_Char( dest_doc, buf[1] );
+ }
+ }
+ fprintf( dest_doc, "\"/>\n" );
+}
+
+
+/****f* Generator/RB_XMLDB_Generate_Char
+ * NAME
+ * RB_XMLDB_Generate_Char
+ * SYNOPSIS
+ * void RB_XMLDB_Generate_Char( FILE * dest_doc, int c )
+ * FUNCTION
+ * Switchboard to RB_XMLDB_Generate_Char
+ * SOURCE
+ */
+
+void RB_XMLDB_Generate_Char(
+ FILE *dest_doc,
+ int c )
+{
+ switch ( c )
+ {
+ case '\n':
+ assert( 0 );
+ break;
+ case '\t':
+ assert( 0 );
+ break;
+ case '<':
+ fprintf( dest_doc, "<" );
+ break;
+ case '>':
+ fprintf( dest_doc, ">" );
+ break;
+ case '&':
+ fprintf( dest_doc, "&" );
+ break;
+ default:
+ // All others are printed literally
+ fputc( c, dest_doc );
+ }
+}
+
+/*****/
+
+void RB_XMLDB_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ fprintf( dest_doc, "<section>\n" );
+ /* fprintf( dest_doc, "<section id=\"%s\">\n", cur_header->unique_name ); */
+ fprintf( dest_doc, "<title>\n" );
+ RB_XMLDB_Generate_String( dest_doc, cur_header->name );
+ fprintf( dest_doc, "</title>\n" );
+}
+
+void RB_XMLDB_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header )
+{
+ USE( cur_header );
+ fprintf( dest_doc, "</section>\n" );
+}
+
+void RB_XMLDB_Generate_Link(
+ FILE *dest,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname )
+{
+ USE( dest_name );
+ USE( filename );
+
+ fprintf( dest, "<link linkend=\"%s\">", labelname );
+ RB_XMLDB_Generate_String( dest, linkname );
+ fprintf( dest, "</link>" );
+}
+
+void RB_XMLDB_Generate_Doc_Start(
+ struct RB_Document *document,
+ FILE *dest_doc,
+ char *charset )
+{
+ if ( course_of_action.do_headless )
+ {
+ /* The user does not want the document head. */
+ }
+ else
+ {
+ fprintf( dest_doc, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
+ charset ? charset : DEFAULT_CHARSET );
+ if ( document->doctype_name && document->doctype_location )
+ {
+ fprintf( dest_doc, "<!DOCTYPE article PUBLIC \"%s\"\n\"%s\">\n",
+ document->doctype_name, document->doctype_location );
+ fprintf( dest_doc, "%s", "<article lang=\"en\">\n" );
+ }
+ else
+ {
+ fprintf( dest_doc, "%s",
+ "<!DOCTYPE article PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\" \n"
+ "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\">\n"
+ "<article lang=\"en\">\n" );
+ }
+ fprintf( dest_doc,
+ "<articleinfo>\n <title>%s</title>\n</articleinfo>\n",
+ document_title ? document_title : DEFAULT_DOCTITILE );
+ }
+}
+
+
+void RB_XMLDB_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name )
+{
+ USE( name );
+
+ if ( course_of_action.do_footless )
+ {
+ /* The user does not want the foot of the
+ * document.
+ */
+ }
+ else
+ {
+ fprintf( dest_doc, "%s", "</article>\n" );
+ }
+}
+
+
+void RB_XMLDB_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name )
+{
+ fprintf( dest_doc, "<formalpara><title>" );
+ RB_XMLDB_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "</title><para></para></formalpara>\n" );
+}
+
+void RB_XMLDB_Generate_Item_Begin(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+
+void RB_XMLDB_Generate_Item_End(
+ FILE *dest_doc )
+{
+ USE( dest_doc );
+ /* Empty */
+}
+
+
+void RB_XMLDB_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( depth );
+
+ fprintf( dest_doc, "%s", "<section>\n<title>" );
+ RB_XMLDB_Generate_String( dest_doc, name );
+ fprintf( dest_doc, "%s", "</title>\n" );
+}
+
+void RB_XMLDB_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name )
+{
+ USE( depth );
+ USE( name );
+ fprintf( dest_doc, "%s", "</section>\n" );
+}
+
+
+void RB_XMLDB_Generate_False_Link(
+ FILE *dest_doc,
+ char *name )
+{
+ RB_XMLDB_Generate_String( dest_doc, name );
+}
+
+
+
+void XMLDB_Generate_Begin_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "<para>\n" );
+}
+
+void XMLDB_Generate_End_Paragraph(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "</para>\n" );
+}
+
+
+void XMLDB_Generate_Begin_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "<literallayout class=\"monospaced\">\n" );
+}
+
+void XMLDB_Generate_End_Preformatted(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "</literallayout>\n" );
+}
+
+
+void XMLDB_Generate_Begin_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "<itemizedlist>" );
+}
+
+void XMLDB_Generate_End_List(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "</itemizedlist>" );
+}
+
+void XMLDB_Generate_Begin_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "<listitem><para>" );
+}
+
+void XMLDB_Generate_End_List_Item(
+ FILE *dest_doc )
+{
+ fprintf( dest_doc, "%s", "</para></listitem>" );
+}
--- /dev/null
+#ifndef ROBODOC_XMLDB_GENERATOR_H
+#define ROBODOC_XMLDB_GENERATOR_H
+/*
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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 "headers.h"
+#include "document.h"
+
+#define MAX_SECTION_DEPTH 7
+
+void RB_XMLDB_Generate_Label(
+ FILE *dest_doc,
+ char *name );
+void RB_XMLDB_Generate_Item_Begin(
+ FILE *dest_doc );
+void RB_XMLDB_Generate_Item_End(
+ FILE *dest_doc );
+void RB_XMLDB_Generate_TOC_2(
+ FILE *dest_doc,
+ struct RB_header **headers,
+ int count );
+void RB_XMLDB_Generate_BeginSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+void RB_XMLDB_Generate_EndSection(
+ FILE *dest_doc,
+ int depth,
+ char *name );
+char *RB_XMLDB_Get_Default_Extension(
+ void );
+void RB_XMLDB_Generate_Doc_Start(
+ struct RB_Document *document,
+ FILE *dest_doc,
+ char *charset );
+void RB_XMLDB_Generate_Doc_End(
+ FILE *dest_doc,
+ char *name );
+void RB_XMLDB_Generate_Header_Start(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_XMLDB_Generate_Header_End(
+ FILE *dest_doc,
+ struct RB_header *cur_header );
+void RB_XMLDB_Generate_Index(
+ struct RB_Document *document,
+ FILE *dest,
+ char *dest_name );
+void RB_XMLDB_Generate_Index_Table(
+ FILE *dest,
+ char *dest_name,
+ int type,
+ char *title );
+void RB_XMLDB_Generate_Empty_Item(
+ FILE *dest );
+void RB_XMLDB_Generate_Link(
+ FILE *dest,
+ char *dest_name,
+ char *filename,
+ char *labelname,
+ char *linkname );
+void RB_XMLDB_Generate_Char(
+ FILE *dest_doc,
+ int c );
+void RB_XMLDB_Generate_Item_Name(
+ FILE *dest_doc,
+ char *name );
+char *RB_XMLDB_RelativeAddress(
+ char *thisname,
+ char *thatname );
+int RB_XMLDB_Generate_Extra(
+ FILE *dest_doc,
+ int item_type,
+ char *cur_char );
+void RB_XMLDB_Generate_False_Link(
+ FILE *dest_doc,
+ char *name );
+
+void XMLDB_Generate_Begin_Paragraph(
+ FILE *dest_doc );
+void XMLDB_Generate_End_Paragraph(
+ FILE *dest_doc );
+void XMLDB_Generate_Begin_Preformatted(
+ FILE *dest_doc );
+void XMLDB_Generate_End_Preformatted(
+ FILE *dest_doc );
+void XMLDB_Generate_Begin_List(
+ FILE *dest_doc );
+void XMLDB_Generate_End_List(
+ FILE *dest_doc );
+void XMLDB_Generate_Begin_List_Item(
+ FILE *dest_doc );
+void XMLDB_Generate_End_List_Item(
+ FILE *dest_doc );
+
+
+#endif /* ROBODOC_XMLDB_GENERATOR_H */
--- /dev/null
+
+o Internationilization
+
+o Implement User defined headers markers. -- DONE
+
+o Implement Scripting interface with Lua.
+
+o Document Scripting interface.
+
+o Move parsing of items from generator.c to analyser.c. -- DONE
+
+o Navigation links in the html documentation. -- DONE
+
+o Document 'accept files:'
+
+o Fix memory leaks.
+
+o More user documentation and more examples.
+
+o More documentation in the code.
+
+o Detect conflicting options and generate error messages
+ for those.
+
+o Implement sanity checks, for instance whether
+ headers exists with the same name, -- DONE
+ or whether the header name
+ occurs in the body (sourcecode) of a SOURCE item.
+
+o Fix bugs listed on source forge.
+
+o create test cases for the wiki text formatting with the
+ test_generator
+
--- /dev/null
+$Id: UPGRADE,v 1.16 2006/06/23 20:50:48 gumpu Exp $
+
+Upgrading to V4.99.26
+ Should work like V4.99.24
+
+Upgrading to V4.99.24
+ If you use the SOURCE item and use a language
+ that has multiline comments, such as C's
+ /* this is a comment
+ on two lines */
+ you will have to add a
+ remark begin markers:
+ and
+ remark end markers:
+ to your robodoc.rc file.
+
+ ROBODoc has the following ones built-in
+ /* */
+ (* *)
+ {* *}
+ <!-- -->
+
+Upgrading to V4.99.10
+ Should work like V4.99.6
+
+Upgrading to V4.99.8
+ Should work like V4.99.6
+
+Upgrading to V4.99.6
+ Should work like V4.0.18
+
+Upgrading to V4.0.18
+ Rename the 'extensions:' block in your robodoc.rc
+ file to 'ignore files:' and read the manual about
+ this new block.
+
+Upgrading to V4.0.12
+ Should work like V4.0.0
+
+Upgrading to V4.0.6
+ Should work like V4.0.0
+
+Upgrading to V4.0.1
+ Should work like V4.0.0
+
+Upgrading to V4.0.0
+ There are many changes compared to V3.2.4:
+ (1) You no longer need a makefile since robodoc
+ can scan a whole directory tree for you.
+ To generate documentation try
+ ./robodoc -src ./YourSourceDir --dest ./ADocDir --mulitdoc --html
+ (2) The ROBODoc defaults file has been changed. It is now
+ called robodoc.rc and has a different format.
+ Please see the manual in Docs/ for more information.
+
+Upgrading to V3.2.4
+ Should work like V3.2.3
+
+Upgrading to V3.2.3
+ Should work like V3.2.2
+
+Upgrading to V3.2.2
+ Add the option HTML to all commands that generate a master index file
+ so use:
+
+ robodoc xrefsfile project_master_index INDEX HTML
+
+ instead of
+
+ robodoc xrefsfile project_master_index INDEX
+
+
+
--- /dev/null
+# Microsoft Developer Studio Project File - Name="robodoc" - Package Owner=<4>
+
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+
+# ** DO NOT EDIT **
+# vi: ff=dos
+
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+
+
+CFG=robodoc - Win32 Debug
+
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+
+!MESSAGE use the Export Makefile command and run
+
+!MESSAGE
+
+!MESSAGE NMAKE /f "robodoc.mak".
+
+!MESSAGE
+
+!MESSAGE You can specify a configuration when running NMAKE
+
+!MESSAGE by defining the macro CFG on the command line. For example:
+
+!MESSAGE
+
+!MESSAGE NMAKE /f "robodoc.mak" CFG="robodoc - Win32 Debug"
+
+!MESSAGE
+
+!MESSAGE Possible choices for configuration are:
+
+!MESSAGE
+
+!MESSAGE "robodoc - Win32 Release" (based on "Win32 (x86) Console Application")
+
+!MESSAGE "robodoc - Win32 Debug" (based on "Win32 (x86) Console Application")
+
+!MESSAGE
+
+
+
+# Begin Project
+
+# PROP AllowPerConfigDependencies 0
+
+# PROP Scc_ProjName ""
+
+# PROP Scc_LocalPath ""
+
+CPP=cl.exe
+
+RSC=rc.exe
+
+
+
+!IF "$(CFG)" == "robodoc - Win32 Release"
+
+
+
+# PROP BASE Use_MFC 0
+
+# PROP BASE Use_Debug_Libraries 0
+
+# PROP BASE Output_Dir "Release"
+
+# PROP BASE Intermediate_Dir "Release"
+
+# PROP BASE Target_Dir ""
+
+# PROP Use_MFC 0
+
+# PROP Use_Debug_Libraries 0
+
+# PROP Output_Dir "Release"
+
+# PROP Intermediate_Dir "Release"
+
+# PROP Target_Dir ""
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+
+# ADD RSC /l 0x809 /d "NDEBUG"
+
+BSC32=bscmake.exe
+
+# ADD BASE BSC32 /nologo
+
+# ADD BSC32 /nologo
+
+LINK32=link.exe
+
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+
+
+!ELSEIF "$(CFG)" == "robodoc - Win32 Debug"
+
+
+
+# PROP BASE Use_MFC 0
+
+# PROP BASE Use_Debug_Libraries 1
+
+# PROP BASE Output_Dir "Debug"
+
+# PROP BASE Intermediate_Dir "Debug"
+
+# PROP BASE Target_Dir ""
+
+# PROP Use_MFC 0
+
+# PROP Use_Debug_Libraries 1
+
+# PROP Output_Dir "Debug"
+
+# PROP Intermediate_Dir "Debug"
+
+# PROP Target_Dir ""
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "RB_MSVC" /D "IGNORE_CASE_FILENAMES" /YX /FD /GZ /c
+
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+
+# ADD RSC /l 0x809 /d "_DEBUG"
+
+BSC32=bscmake.exe
+
+# ADD BASE BSC32 /nologo
+
+# ADD BSC32 /nologo
+
+LINK32=link.exe
+
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+
+
+!ENDIF
+
+
+
+# Begin Target
+
+
+
+# Name "robodoc - Win32 Release"
+
+# Name "robodoc - Win32 Debug"
+
+# Begin Group "Source Files"
+
+
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\analyser.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\ascii_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\directory.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\document.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\file.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\folds.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\globals.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\headers.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\headertypes.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\html_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\items.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\latex_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\links.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\part.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\path.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\roboconfig.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\robodoc.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\rtf_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\sgmldocbook_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\test_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\troff_generator.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\util.c
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\xmldocbook_generator.c
+
+# End Source File
+
+# End Group
+
+# Begin Group "Header Files"
+
+
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\analyser.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\ascii_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\config.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\directory.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\document.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\file.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\folds.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\globals.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\headers.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\headertypes.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\html_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\items.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\latex_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\links.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\part.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\path.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\roboconfig.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\robodoc.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\rtf_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\sgmldocbook_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\test_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\troff_generator.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\util.h
+
+# End Source File
+
+# Begin Source File
+
+
+
+SOURCE=..\Source\xmldocbook_generator.h
+
+# End Source File
+
+# End Group
+
+# Begin Group "Resource Files"
+
+
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+
+# End Group
+
+# End Target
+
+# End Project
+
--- /dev/null
+Microsoft Developer Studio Workspace File, Format Version 6.00
+
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+# vi: ff=dos
+
+
+###############################################################################
+
+
+
+Project: "robodoc"=".\robodoc.dsp" - Package Owner=<4>
+
+
+
+Package=<5>
+
+{{{
+
+}}}
+
+
+
+Package=<4>
+
+{{{
+
+}}}
+
+
+
+###############################################################################
+
+
+
+Global:
+
+
+
+Package=<5>
+
+{{{
+
+}}}
+
+
+
+Package=<3>
+
+{{{
+
+}}}
+
+
+
+###############################################################################
+
+
+
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "robodoc", "robodoc.vcproj", "{57581318-A3F3-4ECD-89E6-688EA649CCC5}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {57581318-A3F3-4ECD-89E6-688EA649CCC5}.Debug.ActiveCfg = Debug|Win32
+ {57581318-A3F3-4ECD-89E6-688EA649CCC5}.Debug.Build.0 = Debug|Win32
+ {57581318-A3F3-4ECD-89E6-688EA649CCC5}.Release.ActiveCfg = Release|Win32
+ {57581318-A3F3-4ECD-89E6-688EA649CCC5}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="robodoc"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;RB_MSVC;IGNORE_CASE_FILENAMES"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/robodoc.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile=".\Debug/robodoc.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/robodoc.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Debug/robodoc.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="2057"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ StringPooling="TRUE"
+ RuntimeLibrary="4"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/robodoc.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile=".\Release/robodoc.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ ProgramDatabaseFile=".\Release/robodoc.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Release/robodoc.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="2057"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="..\Source\analyser.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\ascii_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\directory.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\document.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\file.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\globals.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\headers.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\headertypes.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\html_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\items.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\latex_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\links.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\optioncheck.c">
+ </File>
+ <File
+ RelativePath="..\Source\part.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\path.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\roboconfig.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\robodoc.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\rtf_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\test_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\troff_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\util.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Source\xmldocbook_generator.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="..\Source\analyser.h">
+ </File>
+ <File
+ RelativePath="..\Source\ascii_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\config.h">
+ </File>
+ <File
+ RelativePath="..\Source\directory.h">
+ </File>
+ <File
+ RelativePath="..\Source\document.h">
+ </File>
+ <File
+ RelativePath="..\Source\file.h">
+ </File>
+ <File
+ RelativePath="..\Source\generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\globals.h">
+ </File>
+ <File
+ RelativePath="..\Source\headers.h">
+ </File>
+ <File
+ RelativePath="..\Source\headertypes.h">
+ </File>
+ <File
+ RelativePath="..\Source\html_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\items.h">
+ </File>
+ <File
+ RelativePath="..\Source\latex_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\links.h">
+ </File>
+ <File
+ RelativePath="..\Source\optioncheck.h">
+ </File>
+ <File
+ RelativePath="..\Source\part.h">
+ </File>
+ <File
+ RelativePath="..\Source\path.h">
+ </File>
+ <File
+ RelativePath="..\Source\roboconfig.h">
+ </File>
+ <File
+ RelativePath="..\Source\robodoc.h">
+ </File>
+ <File
+ RelativePath="..\Source\rtf_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\test_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\troff_generator.h">
+ </File>
+ <File
+ RelativePath="..\Source\util.h">
+ </File>
+ <File
+ RelativePath="..\Source\xmldocbook_generator.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+#!/usr/bin/perl -w
+
+# $Id: bump.pl,v 1.44 2007/07/10 19:13:51 gumpu Exp $
+#
+#
+use strict;
+use IO::Dir;
+use IO::File;
+
+my $month = "Jul";
+my $major = 4;
+my $minor = 99;
+my $revision = 36;
+my $release = 1;
+
+sub readme {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+ foreach my $line (@{$lines}) {
+ if ($line =~ m/^ROBODoc\sVersion/) {
+ print $line;
+ $line =~ s/\d+\.\d+\.\d+\s\S+\s2/$major.$minor.$revision $month 2/;
+ print $line;
+ print $file $line;
+ ++$done;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+sub robodoc_h {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+ foreach my $line (@{$lines}) {
+ if ($line =~ m/define\sVERSION\s"/) {
+ ++$done;
+ $line = "#define VERSION \"$major.$minor.$revision\"\n";
+ print $line;
+ print $file $line;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+sub configure_in {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+ foreach my $line (@{$lines}) {
+ if ($line =~ m/AM_INIT_AUTOMAKE/) {
+ ++$done;
+ $line = "AM_INIT_AUTOMAKE(robodoc, $major.$minor.$revision)\n";
+ print $line;
+ print $file $line;
+ } elsif ($line =~ m/AC_INIT/) {
+ ++$done;
+ $line = "AC_INIT(robodoc, $major.$minor.$revision)\n";
+ print $line;
+ print $file $line;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 2);
+}
+
+
+sub robodoc_1 {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+ foreach my $line (@{$lines}) {
+ if ($line =~ m/TH\sROBODoc/) {
+ print $line;
+ $line =~ s/ROBODoc\s\d+\.\d+\.\d+/ROBODoc $major.$minor.$revision/;
+ $line =~ s/"\S+\s2/"$month 2/;
+ print $line;
+ print $file $line;
+ ++$done;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+
+sub manual {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+ foreach my $line (@{$lines}) {
+ if ($line =~ m/title>ROBODoc\s\d/) {
+ print $line;
+ $line =~ s/title>ROBODoc\s\d+\.\d+\.\d+/title>ROBODoc $major.$minor.$revision/;
+ print $line;
+ print $file $line;
+ ++$done;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+sub rpm_mk {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+
+ foreach my $line (@{$lines}) {
+ if ($line =~ /^PROJECT\_VERSION/) {
+ print $line;
+ $line =~ s/\d+\.\d+\.\d+/$major.$minor.$revision/;
+ print $line;
+ print $file $line;
+ ++$done;
+ } elsif ($line =~ /^PROJECT\_RELEASE/) {
+ print $line;
+ $line =~ s/\d/$release/;
+ print $line;
+ print $file $line;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+sub makefile_plain {
+ my $file = shift;
+ my $lines = shift;
+ my $done = 0;
+
+ foreach my $line (@{$lines}) {
+ if ($line =~ /^VERS/) {
+ print $line;
+ $line =~ s/\d+\.\d+\.\d+/$major.$minor.$revision/;
+ print $line;
+ print $file $line;
+ ++$done;
+ } elsif ($line =~ /^RELEASE/) {
+ print $line;
+ $line =~ s/\d/$release/;
+ print $line;
+ print $file $line;
+ } else {
+ print $file $line;
+ }
+ }
+ die if ($done != 1);
+}
+
+my %updaters = ();
+
+$updaters{"Docs/manual.xml"} = \&manual;
+$updaters{"Docs/robodoc.1"} = \&robodoc_1;
+$updaters{"Source/robodoc.h"} = \&robodoc_h;
+$updaters{"README"} = \&readme;
+$updaters{"configure.in"} = \&configure_in;
+$updaters{"rpm.mk"} = \&rpm_mk;
+$updaters{"Source/makefile.plain"} = \&makefile_plain;
+
+foreach my $filename ( keys %updaters ) {
+ print $filename, "\n";
+ my $file = IO::File->new("<$filename") or die $filename;
+ my @lines = <$file>;
+ $file->close();
+ $file = IO::File->new(">$filename.bup") or die $filename;
+ $updaters{$filename}( $file, \@lines );
+ $file->close();
+ rename "$filename.bup", $filename;
+}
+
+__DATA__
+Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
+Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
+Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
+
+This file is part of ROBODoc
+
+ROBODoc 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/>.
+
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+
+dnl TODO FS Document this.
+dnl TODO FS Figure out how this really works.
+
+AC_INIT(robodoc, 4.99.36)
+AC_CONFIG_SRCDIR(Source/robodoc.h)
+AC_DEFINE_UNQUOTED([ROBO_PREFIX],"$prefix", [default location for robodoc.rc])
+
+AM_CONFIG_HEADER(Source/config.h)
+AM_INIT_AUTOMAKE(robodoc, 4.99.36)
+
+AC_PROG_MAKE_SET
+
+dnl Checks for programs.
+AC_PROG_CC
+
+dnl Do a cross compile under cygwin
+if test "`uname -o 2>/dev/null`" = Cygwin; then
+ CFLAGS="$CFLAGS -mno-cygwin";
+fi
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CANONICAL_HOST
+
+dnl Test for Mac OS X
+case ${host} in
+*-apple-darwin*)
+ OSTYPE="DARWIN"
+
+ ;;
+esac
+
+AM_CONDITIONAL([FINK], [test "x$OSTYPE" = "xDARWIN"])
+
+dnl Add DARWIN flag for Mac OS X
+if test "x$OSTYPE" = "xDARWIN";then
+ CFLAGS="$CFLAGS -DDARWIN"
+fi
+
+
+dnl The value of --prefix is used in the sources
+dnl so make a define for it.
+
+
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+dnl Checks for library functions.
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+AC_FUNC_FORK
+AC_CHECK_FUNCS(strstr)
+
+AC_CONFIG_FILES([makefile Docs/makefile Docs/robodoc.1 Source/makefile])
+AC_OUTPUT
--- /dev/null
+#!/bin/bash
+# vi: ff=unix spell
+# $Id: do.sh,v 1.19 2007/02/06 23:25:34 gumpu Exp $
+
+# If a ./do.sh under cygwin gives
+# : command not found
+# : command not found
+# : command not foundclocal
+#
+# Then this file has wrong line-endings (cr/lf).
+# To fix it unzip the archive under cygwin with the
+# option -a, so
+# unzip -a robodoc-xx-xx-xx.zip
+#
+
+rm -f *~
+rm -f makefile.in
+rm -f *.tar.gz *.zip
+rm -f *.log aclocal.m4 config.cache
+rm -fr autom4te.cache
+rm -f install-sh
+rm -f mkinstalldirs
+rm -f missing makefile
+rm -f configure config.status
+
+aclocal
+automake -a
+autoconf
+#CFLAGS="-g -Wall -DDMALLOC -DMALLOC_FUNC_CHECK" LDFLAGS="-ldmalloc" ./configure
+#CFLAGS="-g -Wall -Wshadow -Wbad-function-cast -Wconversion -Wredundant-decls" ./configure
+CFLAGS="-g -Wall" ./configure
+make dist
+make dist-zip
+make clean
+make
+
+# cross-compile if all tools are found
+(cd Source && make -f makefile.plain xcompiler-test) > /dev/null 2>&1
+if [ "$?" = "0" ] ; then
+ set -e
+ rm -fr ./tmp$$
+ cp -R ./Source ./tmp$$
+ cd ./tmp$$
+ make -f makefile.plain xcompile
+ cp -f *.zip ../
+ cd ..
+ rm -fr ./tmp$$
+ set +e
+fi
+
+# Mac OS X package
+
+if [ "`uname`" = "Darwin" ] ; then
+ make -f rpm.mk osxpkg
+fi
+
+exit 0
--- /dev/null
+#!/usr/bin/perl -w
+
+# $Id: dos2unix.pl,v 1.2 2007/04/02 20:29:20 gumpu Exp $
+#
+# Convert all files to the unix-line-end convention.
+# This ensures that all files in the ROBODoc package
+# follow the same convention.
+#
+use strict;
+use IO::File;
+use IO::Dir;
+
+
+sub fix
+{
+ my $path = shift;
+ my $mode = shift;
+ my $file;
+
+ if ( -T $path ) {
+ if ( !( $file = IO::File->new($path, "r") ) ) {
+ print "Can't open $path to read : $!\n";
+ } else {
+ # Open in binmode otherwise Perl will do the cr/lf for
+ # us while reading.
+ binmode( $file );
+ my @file_data = <$file>;
+ $file->close();
+ if ( !( grep { /\r/ } @file_data ) ) {
+ print "$path is OK\n";
+ # File is OK
+ } else {
+ print "$path contains CR/LF\n";
+ if ( $mode eq "test" ) {
+ print "$path contains CR/LF\n";
+ } else {
+ print "Fixing: $path\n";
+ map { s/\r//g; } (@file_data);
+ $file = IO::File->new("> $path") or die "Can't open $path to write : $!";
+ binmode( $file );
+ print $file @file_data;
+ $file->close();
+ }
+ }
+ }
+ }
+}
+
+sub scan_directory
+{
+ my $dirname = shift;
+ my $mode = shift;
+ my $path;
+ my $dir = IO::Dir->new($dirname) or die "Can't open $dirname : $!";
+ my @files = $dir->read();
+ $dir->close();
+
+ foreach my $filename ( sort @files ) {
+ $path = "$dirname/$filename";
+ if ( -f $path ) {
+ fix( $path, $mode );
+ }
+ }
+
+ # Also fix any subdirectories.
+ foreach my $subdirname ( sort @files ) {
+ $path = "$dirname/$subdirname";
+ if ( -d $path ) {
+ unless ( $subdirname =~ m/^\.+$/ ) {
+ scan_directory( $path, $mode );
+ }
+ }
+ }
+}
+
+sub main
+{
+ my $out = IO::File->new(">fl.txt") or die "can't open fl.txt : $!";
+ scan_directory( ".", "test" );
+ $out->close();
+}
+
+main;
+
--- /dev/null
+# vi: ff=unix
+## Process this file with automake to produce Makefile.in
+# $Id: makefile.am,v 1.27 2007/02/06 23:25:34 gumpu Exp $
+#
+# Information for automake
+# TODO: Figure out how this works, and how this works with
+# the different version of the automake tool set.
+#
+
+AUTOMAKE_OPTIONS = dist-zip
+
+SUBDIRS = Source Docs
+
+# TODO: if we define docdir configure.in do we still need to define
+# it here and in Docs/makefile.am ?
+
+#if FINK
+#docdir = $(prefix)/share/doc/$(PACKAGE)
+#else
+#docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+#endif
+
+doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README UPGRADE
+
+if FINK
+contributiondir = ${docdir}/Contributions
+dist_contribution_DATA = \
+ Contributions/robodoc2pod.pl \
+ Contributions/robodoc.kaptn
+exampledir = ${docdir}/Examples/PerlExample
+dist_example_DATA = \
+ Examples/PerlExample/robodoc.rc
+examplesubdir = ${docdir}/Examples/PerlExample/Source
+dist_examplesub_DATA = \
+ Examples/PerlExample/Source/Box/RectangularBox.pm \
+ Examples/PerlExample/Source/Box/SquareBox.pm \
+ Examples/PerlExample/Source/Box.pm \
+ Examples/PerlExample/Source/Cargo.txt \
+ Examples/PerlExample/Source/Loader.txt \
+ Examples/PerlExample/Source/SmartLoader.pm \
+ Examples/PerlExample/Source/TruckPacker.pl
+endif
+
+if FINK
+EXTRA_DIST = \
+ UPGRADE \
+ Docs/robodoc.1 \
+ Docs/robohdrs.1 \
+ Docs/robodoc.rc \
+ Docs/manual.html \
+ Docs/manual.css
+else
+EXTRA_DIST = \
+ UPGRADE \
+ Docs/robodoc.1 \
+ Docs/robohdrs.1 \
+ Docs/robodoc.rc \
+ Docs/manual.html \
+ Docs/manual.css \
+ Contributions/robodoc2pod.pl \
+ Contributions/robodoc.kaptn \
+ Source/makefile.plain \
+ Source/makefile.bcc \
+ Source/makefile.wingcc \
+ Source/makefile.mingw \
+ Source/makefile.mingw-cygwin \
+ Examples/PerlExample/robodoc.rc \
+ Examples/PerlExample/Source/Box/RectangularBox.pm \
+ Examples/PerlExample/Source/Box/SquareBox.pm \
+ Examples/PerlExample/Source/Box.pm \
+ Examples/PerlExample/Source/Cargo.txt \
+ Examples/PerlExample/Source/Loader.txt \
+ Examples/PerlExample/Source/SmartLoader.pm \
+ Examples/PerlExample/Source/TruckPacker.pl \
+ Win32/robodoc.dsp \
+ Win32/robodoc.dsw \
+ Win32/robodoc.sln \
+ Win32/robodoc.vcproj
+endif
+
+#
+# End of automake stuff
+#
--- /dev/null
+{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx1440\tx2880\tx4320\tx5760\tx7200\ql\qnatural
+
+\f0\fs24 \cf0 ROBODoc Version PROJECT_VERSION (release PROJECT_RELEASE) -- autodocs formatter Copyright (C) 1994-2005 Frans Slothouber, Petteri Kettunen, and Jacco van Weert.\
+\
+This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\
+\
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
+\
+You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or see online version from URL http://www.gnu.org/copyleft/gpl.html\
+\
+}
--- /dev/null
+{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx1440\tx2880\tx4320\tx5760\tx7200\ql\qnatural
+
+\f0\fs24 \cf0 This is commandline tool. For help on various options and usage, launch Terminal.App from /Applications/Utilities and type:\
+\
+ robodoc --help\
+\
+Documentation and examples will be installed to /usr/share/doc/robodoc-PROJECT_VERSION directory. See ROBODoc homepage http://www.xs4all.nl/~rfsber/Robo/index.html or http://sourceforge.net/projects/robodoc/ for more documentation and updates.\
+\
+}
\ No newline at end of file
--- /dev/null
+{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx1440\tx2880\tx4320\tx5760\tx7200\ql\qnatural
+
+\f0\fs24 \cf0 Welcome to the ROBODoc installation program. You will be guided through the steps necessary to install this software.}
--- /dev/null
+#!/bin/sh
+# $Id: dmg.sh,v 1.4 2005/08/28 13:08:50 petterik Exp $
+#****h* jpgind/dmg.sh
+# NAME
+# dmg.sh
+# SYNOPSIS
+# dmg.sh <project_name> <project_version>
+# PURPOSE
+# Shell script for wrapping up mountable Mac OS X disk image
+# containing a native installation package.
+# SOURCE
+#
+
+set -e
+
+NAM=$1
+VER=$2
+REL=$3
+
+if ([ "$NAM" = "" ] || [ "$VER" = "" ]) ; then
+ echo "usage: dmg.sh <project name> <project version>"
+ exit 1
+fi
+
+if [ "$REL" = "" ] ; then
+ REL=1
+fi
+
+PRJ=${NAM}-${VER}.pkg
+TMPIMG=$$-${PRJ}.dmg
+DSKNAM=${NAM}-${VER}-${REL}
+
+if [ ! -d $PRJ ] ; then
+ echo " Directory $PRJ does not exist"
+ exit 1
+fi
+
+for f in $TMPIMG ${DSKNAM}.dmg ${DSKNAM}.dmg.gz ; do
+ if [ -f $f ] ; then
+ rm -f $f
+ fi
+done
+
+#
+BLKS=`du -s $PRJ | awk '{print $1}'`
+
+echo " $PRJ blocks $BLKS"
+
+# man hdutil example adds 100 sectors, min size is 4MB
+if [ $BLKS -lt 8192 ] ; then
+ BLKS=8192
+else
+ BLKS=`expr $BLKS \+ 100`
+fi
+
+echo " Using $BLKS sectors for raw image"
+
+hdiutil create $TMPIMG -sectors $BLKS -layout NONE
+
+# Create a /dev/disk device from the image
+drive=`hdid -nomount $TMPIMG | awk '{print $1}'`
+
+# Create a new filesystem on the disk device
+newfs_hfs -v "${DSKNAM}" -b 4096 /dev/r${drive:t}
+
+# Remount the disk
+echo " Image formatted, ejecting ${drive}..."
+hdiutil eject ${drive}
+
+echo " Mounting $TMPIMG ..."
+drive=`hdid $TMPIMG | awk '{print $1}'`
+
+echo " Searching for ${drive}..."
+while [ "$MOUNTPOINT" = "" ] ; do
+ MOUNTPOINT=`df -l | grep $drive | awk '{print $6}'`
+done
+echo " Found $drive at $MOUNTPOINT"
+
+# Unpack the tarball into the mounted filesystem
+echo " Copying application..."
+
+tar cf - $PRJ | (cd $MOUNTPOINT; tar xf -)
+
+# Eject the disk
+echo " Ejecting..."
+hdiutil eject ${drive}
+
+# Convert the image to a UDIF compressed image
+echo " Compressing..."
+hdiutil convert -format UDCO $TMPIMG -o ${DSKNAM}.dmg
+gzip ${DSKNAM}.dmg
+
+# Remove the temporary image
+echo " Removing scratch image"
+rm -f $TMPIMG
+
+ls -l ${DSKNAM}.dmg.gz
+
+md5 ${DSKNAM}.dmg.gz
+
+exit 0
+
+#***** end dmg.sh
--- /dev/null
+# $Id: robodoc.spec.in,v 1.1 2003/03/22 15:49:10 petterik Exp $
+Summary: Tool to extract documentation from source code
+Name: robodoc
+Version: PROJECT_VERSION
+Release: PROJECT_RELEASE
+Copyright: GPL
+Group: Development/Tools
+Source: %{name}-%{version}.tar.gz
+URL: http://www.xs4all.nl/~rfsber/Robo/index.html
+BuildRequires: xmlto >= 0.0.10-4
+BuildRoot: %{_tmppath}/%{name}-root
+
+%description
+ROBODoc is a documentation tool. It extracts the documentation from comment
+headers in the source code and formats it in HTML, RTF, TeX, or ASCII. Works
+with C, Tcl, FORTRAN, and any other language that supports remarks or comments.
+
+%prep
+%setup -q
+
+%build
+aclocal
+automake -a
+autoconf
+%configure
+make %{_smp_mflags}
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}/%{_datadir}/%{name}/Headers
+mkdir -p %{buildroot}/%{_bindir}
+mkdir -p %{buildroot}%{_mandir}/man1
+
+install -s -m 755 Source/robodoc %{buildroot}/%{_bindir}
+install -s -m 755 Source/robohdrs %{buildroot}/%{_bindir}
+install -s -m 755 Docs/robodoc.1 %{buildroot}%{_mandir}/man1
+install -s -m 755 Docs/robohdrs.1 %{buildroot}%{_mandir}/man1
+install -s -m 644 Headers/* %{buildroot}/%{_datadir}/%{name}/Headers
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS ChangeLog COPYING DEVELOPERS INSTALL NEWS README TODO UPGRADE Docs/manual.html
+%{_bindir}/*
+%{_mandir}/man1/*
+%{_datadir}/%{name}/Headers/*
+
+%changelog
+* Sun Mar 23 2003 petterik <petterik@users.sourceforge.net>
+- renamed robodoc.spec to robodoc.spec.in
+
+* Mon Feb 24 2003 petterik <petterik@users.sourceforge.net>
+- removed conversion of manual.xml to manual.html (incl in Docs/makefile)
+
+* Sat Feb 15 2003 Petteri Kettunen http://www.iki.fi/petterik/
+- manual.html build, Headers examples installed
+- BuildRequires xmlto
+
+* Mon Feb 10 2003 Petteri Kettunen http://www.iki.fi/petterik/
+- RH8.0 -specific changes
+- robohdrs binary and manual pages added
+
+* Fri Jan 03 2003 Petteri Kettunen http://www.iki.fi/petterik/
+- skeleton RPM
--- /dev/null
+#****h* ROBODoc/rpm.mk
+# NAME
+# rpm.mk
+# SYNOPSIS
+# make -f rpm.mk
+# make -f rpm.mk osxpkg
+# PURPOSE
+# Easy way to make RPM package. Contains rules also for building a package
+# for Mac OS X.
+# MODIFICATION HISTORY
+# 2003-03-23/PetteriK: robodoc.spec.in template
+# 2003-02-10/PetteriK: RPM build using RH8.0, remote build dropped
+# SEE ALSO
+# http://www.rpm.org/RPM-HOWTO/build.html
+# SOURCE
+#
+
+# rpm-related parameters
+PROJECT_NAME = robodoc
+PROJECT_VERSION = 4.99.36
+PROJECT_RELEASE = 1
+PROJECT = $(PROJECT_NAME)-$(PROJECT_VERSION)
+SPECFILE = $(PROJECT_NAME).spec
+TARBALL = $(PROJECT).tar.gz
+SRPM = $(PROJECT)-$(PROJECT_RELEASE).src.rpm
+RPM = $(PROJECT)-$(PROJECT_RELEASE).i386.rpm
+RHDIR = $(HOME)/redhat
+
+all: robodoc.spec rpm
+
+#****** rpm.mk/robodoc.spec
+# NAME
+# robodoc.spec
+# FUNCTION
+# Fill in version and release info to .spec file template.
+# SOURCE
+
+robodoc.spec: robodoc.spec.in rpm.mk
+ rm -f robodoc.spec
+ sed -e 's/PROJECT_VERSION/$(PROJECT_VERSION)/g' \
+ -e 's/PROJECT_RELEASE/$(PROJECT_RELEASE)/g' \
+ robodoc.spec.in > robodoc.spec
+
+#****
+
+#****** rpm.mk/srcpkg
+# NAME
+# srcpkg --
+# FUNCTION
+# Make pretty tarball of sources in this directory tree.
+# PREQUISITIES
+# make_rhdir
+# SOURCE
+
+.PHONY: srcpkg
+srcpkg: make_rhdir
+ cp -R . /tmp/$(PROJECT)
+ find /tmp/$(PROJECT) -type d -name CVS | xargs rm -fr
+ find /tmp/$(PROJECT) -type d -name .gone | xargs rm -fr
+ (cd /tmp; tar cf - $(PROJECT) | gzip -c > $(RHDIR)/SOURCES/$(TARBALL))
+ rm -fr /tmp/$(PROJECT)
+
+#****
+
+#****** rpm.mk/rpm
+# NAME
+# rpm --
+# FUNCTION
+# Make RPM; both binary and source packages.
+# PREQUISITIES
+# make_rhdir, srcpkg
+# SOURCE
+
+.PHONY: rpm
+rpm: srcpkg
+ cp $(SPECFILE) $(RHDIR)/SPECS/$(SPECFILE)
+ rpmbuild -ba $(RHDIR)/SPECS/$(SPECFILE)
+ @echo "RPM packages are now:"
+ @ls -l $(RHDIR)/RPMS/i386/$(RPM)
+ @ls -l $(RHDIR)/SRPMS/$(SRPM)
+
+#****
+
+#****** rpm.mk/make_rhdir
+# NAME
+# make_rhdir --
+# FUNCTION
+# Create directories for RPM build. Topdir parameter in ~/.rpmmacros
+# has to point to $(HOME)/redhat (default but users may change that).
+# SOURCE
+
+.PHONY: make_rhdir
+make_rhdir:
+ for i in $(RHDIR) $(RHDIR)/RPMS $(RHDIR)/SOURCES $(RHDIR)/SPECS $(RHDIR)/SRPMS $(RHDIR)/BUILD; do \
+ if [ ! -d $$i ] ; then \
+ mkdir $$i; \
+ fi; \
+ done;
+
+#****
+
+#****** rpm.mk/osxpkg
+# NAME
+# osxpkg
+# FUNCTION
+# Create Mac OS X binary package.
+# SEE ALSO
+# dmg.sh
+# SOURCE
+
+MACPKG=$(PROJECT).pkg
+PKGINF=$(MACPKG)/Contents/Resources/English.lproj/$(PROJECT).info
+
+.PHONY: osxpkg
+osxpkg:
+ rm -f osx/License.rtf osx/ReadMe.rtf
+ sed -e 's/PROJECT_VERSION/$(PROJECT_VERSION)/g' \
+ -e 's/PROJECT_RELEASE/$(PROJECT_RELEASE)/g' \
+ osx/License.rtf.in > osx/License.rtf
+ sed -e 's/PROJECT_VERSION/$(PROJECT_VERSION)/g' \
+ -e 's/PROJECT_RELEASE/$(PROJECT_RELEASE)/g' \
+ osx/ReadMe.rtf.in > osx/ReadMe.rtf
+ aclocal
+ automake -a
+ autoconf
+ ./configure
+ $(MAKE)
+ rm -fr Distribution_Folder $(MACPKG)
+ mkdir -p Distribution_Folder/Package_Root/private/etc
+ mkdir -p Distribution_Folder/Package_Root/Applications
+ mkdir -p Distribution_Folder/Package_Root/usr/share/doc/$(PROJECT)/Headers
+ mkdir -p Distribution_Folder/Package_Root/usr/bin
+ mkdir -p Distribution_Folder/Package_Root/usr/share/man/man1
+ mkdir -p Distribution_Folder/Resources/PreFlight
+ mkdir -p Distribution_Folder/Resources/PostFlight
+ -chmod a+rx Distribution_Folder/Resources/PreFlight/* Distribution_Folder/Resources/PostFlight/*
+ cp Source/robodoc Distribution_Folder/Package_Root/usr/bin
+ cp Source/robohdrs Distribution_Folder/Package_Root/usr/bin
+ cp Docs/robodoc.1 Distribution_Folder/Package_Root/usr/share/man/man1
+ cp Docs/robohdrs.1 Distribution_Folder/Package_Root/usr/share/man/man1
+ chmod 755 Distribution_Folder/Package_Root/usr/bin/*
+ chmod 644 Distribution_Folder/Package_Root/usr/share/man/man1/*
+ cp AUTHORS ChangeLog COPYING DEVELOPERS INSTALL NEWS README TODO UPGRADE Distribution_Folder/Package_Root/usr/share/doc/$(PROJECT)
+ cp Headers/*.sample Distribution_Folder/Package_Root/usr/share/doc/$(PROJECT)/Headers
+ mkdir -p $(MACPKG)/Contents/Resources/PostFlight
+ mkdir -p $(MACPKG)/Contents/Resources/PreFlight
+ mkdir -p $(MACPKG)/Contents/Resources/English.lproj
+ cp osx/*.rtf $(MACPKG)/Contents/Resources
+ (cd Distribution_Folder; mkbom Package_Root ../$(MACPKG)/Contents/Resources/$(PROJECT).bom)
+ (cd Distribution_Folder/Package_Root; pax -w -f ../../$(MACPKG)/Contents/Resources/$(PROJECT).pax .)
+ (cd $(MACPKG)/Contents/Resources; gzip $(PROJECT).pax)
+ echo "Numfiles 20" > $(MACPKG)/Contents/Resources/$(PROJECT).sizes
+ echo "InstalledSize 352" >> $(MACPKG)/Contents/Resources/$(PROJECT).sizes
+ echo "CompressedSize 352" >> $(MACPKG)/Contents/Resources/$(PROJECT).sizes
+ echo "Title ROBODoc" > $(PKGINF)
+ echo "Version 4.0.0" >> $(PKGINF)
+ echo "Description Autdocs formatter" >> $(PKGINF)
+ echo "DefaultLocation /" >> $(PKGINF)
+ echo "DeleteWarning" >> $(PKGINF)
+ echo "" >> $(PKGINF)
+ echo "### Package Flags" >> $(PKGINF)
+ echo "" >> $(PKGINF)
+ echo "NeedsAuthorization YES" >> $(PKGINF)
+ echo "Relocatable NO" >> $(PKGINF)
+ echo "RequiresReboot NO" >> $(PKGINF)
+ echo "UseUserMask NO" >> $(PKGINF)
+ echo "OverwritePermissions NO" >> $(PKGINF)
+ echo "InstallFat NO" >> $(PKGINF)
+ echo -n pmkrpkg1 > $(MACPKG)/Contents/PkgInfo
+ sh osx/dmg.sh $(PROJECT_NAME) $(PROJECT_VERSION) $(PROJECT_RELEASE)
+ rm -fr Distribution_Folder $(MACPKG)
+
+#****
+
+# date format for spec file
+.PHONY: logdate
+logdate:
+ echo "`date +'* %a %b %d %Y'` `whoami`"
+
+.PHONY: chkpkg
+chkpkg:
+ rpm -qpi $(RHDIR)/RPMS/i386/$(RPM)
+ rpm -qpl $(RHDIR)/RPMS/i386/$(RPM)
+
+.PHONY: chksys
+chksys:
+ rpm -qa | grep $(PROJECT_NAME)
+
+.PHONY: test
+test: install chksys uninstall
+
+.PHONY: install
+install:
+ sudo rpm -i $(RHDIR)/RPMS/i386/$(RPM)
+
+.PHONY: uninstall
+uninstall:
+ sudo rpm -e $(PROJECT_NAME)
+
+#****
--- /dev/null
+#!/usr/bin/perl -w
+# vi: ff=unix spell
+#
+# Usage:
+#
+# todo.pl [filenames]
+#
+#
+
+use strict;
+use IO::Dir;
+use IO::File;
+
+my $count = 0;
+
+sub scan_file {
+ my $filename = shift;
+ unless ( $filename =~ m/tags$/ ) {
+
+ my $file = IO::File->new("<$filename") or die "can't open $filename";
+ my %todos;
+ my $nr = 1;
+
+ # Scan for TODOs
+ while (my $line = <$file>) {
+ # The sentence after the TODO ~should start with a letter.
+ # This ensures we skip the TODO's like the one below.
+ if ($line =~ /TODO(|:)\s+[A-Za-z]/i) {
+ $todos{$nr} = $line;
+ }
+ ++$nr;
+ }
+ $file->close();
+
+ # Show results.
+ if (scalar(keys %todos)) {
+ foreach my $key (sort { $a <=> $b } keys %todos) {
+ my $line = $todos{$key};
+ if ($line =~ m/TODO(.*)$/i) {
+ my $comment = $1;
+ $comment =~ s/^(:|\s)+//;
+ $comment =~ s/\*\/\s*$//;
+ # Print as: foobar.c(10) The stuff to be done
+ printf( "%-30s %s\n", "$filename($key)", $comment );
+ ++$count;
+ }
+ }
+ }
+
+ }
+}
+
+
+# Scan all the files in a directory.
+# Then repeat the process for all the subdirectories.
+#
+
+sub scan_directory {
+ my $dirname = shift;
+
+ my $dir = IO::Dir->new($dirname) or die "can't open $dirname : $!";
+ my @files = $dir->read();
+ $dir->close();
+
+ my @source_files = grep { -T "$dirname/$_" } @files;
+
+ foreach my $filename ( sort @source_files ) {
+ my $path = "$dirname/$filename";
+ $path =~ s/\.\///;
+ scan_file( $path );
+ }
+
+ # Repeat the process for all subdirectories.
+ foreach my $filename ( sort @files ) {
+ my $path = "$dirname/$filename";
+ if ( ( -d $path ) and ( $filename !~ m/^\./ ) ) {
+ scan_directory( $path );
+ }
+ }
+}
+
+
+sub main {
+ if (@ARGV) {
+ # We are given a set of file names on the command line.
+ foreach my $file (grep { -T } @ARGV) {
+ scan_file( $file );
+ }
+ } else {
+ # No parameters, scan the current directory and
+ # all its subfolders.
+ scan_directory( '.' );
+ }
+ print $count, " TODOs to go\n";
+
+}
+
+main;
+
+