Imported Robodoc.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 2 Feb 2008 18:54:01 +0000 (19:54 +0100)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 2 Feb 2008 18:54:01 +0000 (19:54 +0100)
263 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
CVS.adm/MstData.stp [new file with mode: 0644]
CVS.adm/UsgData.stp [new file with mode: 0644]
CVS.adm/prjprm.stp [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Contributions/robodoc.kaptn [new file with mode: 0644]
Contributions/robodoc2pod.pl [new file with mode: 0644]
DEVELOPERS [new file with mode: 0644]
Design/design.txt [new file with mode: 0644]
Design/uml.fig [new file with mode: 0644]
Docs/bugs.xml [new file with mode: 0644]
Docs/configuration.xml [new file with mode: 0644]
Docs/docbook-simple/sdocbook.dtd [new file with mode: 0644]
Docs/example_makefile [new file with mode: 0644]
Docs/examples.xml [new file with mode: 0644]
Docs/extracting.xml [new file with mode: 0644]
Docs/faq.dat [new file with mode: 0644]
Docs/faqfooter.html [new file with mode: 0644]
Docs/faqheader.html [new file with mode: 0644]
Docs/header_specs.pl [new file with mode: 0644]
Docs/installing.xml [new file with mode: 0644]
Docs/main.css [new file with mode: 0644]
Docs/makefile.am [new file with mode: 0644]
Docs/makefile.cygwin [new file with mode: 0644]
Docs/makefile.plain [new file with mode: 0644]
Docs/makefile.win32 [new file with mode: 0644]
Docs/manual-cygwin.xsl [new file with mode: 0644]
Docs/manual.css [new file with mode: 0644]
Docs/manual.html [new file with mode: 0644]
Docs/manual.xml [new file with mode: 0644]
Docs/manual.xsl [new file with mode: 0644]
Docs/manual_html_help-cygwin.xsl [new file with mode: 0644]
Docs/manual_html_help.xsl [new file with mode: 0644]
Docs/manual_html_help_win32.xsl [new file with mode: 0644]
Docs/manual_win32.xsl [new file with mode: 0644]
Docs/options.xml [new file with mode: 0644]
Docs/preface.xml [new file with mode: 0644]
Docs/preparing.xml [new file with mode: 0644]
Docs/robodoc.1 [new file with mode: 0644]
Docs/robodoc.1.in [new file with mode: 0644]
Docs/robodoc.rc [new file with mode: 0644]
Docs/robohdrs.1 [new file with mode: 0644]
Docs/tips.xml [new file with mode: 0644]
Examples/PerlExample/Doc/robo_classes.html [new file with mode: 0644]
Examples/PerlExample/Doc/robo_methods.html [new file with mode: 0644]
Examples/PerlExample/Doc/robo_modules.html [new file with mode: 0644]
Examples/PerlExample/Doc/robo_sourcefiles.html [new file with mode: 0644]
Examples/PerlExample/Source/Box.pm [new file with mode: 0644]
Examples/PerlExample/Source/Box/RectangularBox.pm [new file with mode: 0644]
Examples/PerlExample/Source/Box/SquareBox.pm [new file with mode: 0644]
Examples/PerlExample/Source/Cargo.txt [new file with mode: 0644]
Examples/PerlExample/Source/Loader.txt [new file with mode: 0644]
Examples/PerlExample/Source/SmartLoader.pm [new file with mode: 0644]
Examples/PerlExample/Source/TruckPacker.pl [new file with mode: 0644]
Examples/PerlExample/robodoc.rc [new file with mode: 0644]
Headers/assembler.sample [new file with mode: 0644]
Headers/basic.sample [new file with mode: 0644]
Headers/c.sample [new file with mode: 0644]
Headers/cpp.sample [new file with mode: 0644]
Headers/forth.sample [new file with mode: 0644]
Headers/fortran.sample [new file with mode: 0644]
Headers/html.sample [new file with mode: 0644]
Headers/makefile [new file with mode: 0644]
Headers/tcl.sample [new file with mode: 0644]
INSTALL [new file with mode: 0644]
INSTALL.Debian [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
Source/.indent.pro [new file with mode: 0644]
Source/Test/FileFilterSrc/README [new file with mode: 0644]
Source/Test/FileFilterSrc/test.pas [new file with mode: 0644]
Source/Test/FileFilterSrc/test2.c [new file with mode: 0644]
Source/Test/FileFilterSrc/test3.c [new file with mode: 0644]
Source/Test/FileFilterSrc/test4.c [new file with mode: 0644]
Source/Test/FileFilterSrc_2/AcceptDir/accept.h [new file with mode: 0644]
Source/Test/FileFilterSrc_2/accept.c [new file with mode: 0644]
Source/Test/FileFilterSrc_2/accept.pl [new file with mode: 0644]
Source/Test/FileFilterSrc_2/ignore.ign [new file with mode: 0644]
Source/Test/FileFilterSrc_2/ignore.xxx [new file with mode: 0644]
Source/Test/ROBOTest.pm [new file with mode: 0644]
Source/Test/RoboHeaders/rbh_test1.dat [new file with mode: 0644]
Source/Test/Scr1/Subdir/ccfile.dat [new file with mode: 0644]
Source/Test/Scr1/aafile.dat [new file with mode: 0644]
Source/Test/Scr1/bbfile.dat [new file with mode: 0644]
Source/Test/Scr2/master_index_test.dat [new file with mode: 0644]
Source/Test/SortSource/sorted.c [new file with mode: 0644]
Source/Test/SortSource/unsorted.c [new file with mode: 0644]
Source/Test/alt_rc_file.rc [new file with mode: 0644]
Source/Test/cmode_test.dat [new file with mode: 0644]
Source/Test/cmode_test1.pl [new file with mode: 0755]
Source/Test/doc_dir_filter_test.pl [new file with mode: 0755]
Source/Test/doc_dir_filter_test.rc [new file with mode: 0644]
Source/Test/encoding_test.dat [new file with mode: 0644]
Source/Test/error_missing_header_end_test.dat [new file with mode: 0644]
Source/Test/error_missing_header_end_test_2.dat [new file with mode: 0644]
Source/Test/filter_test_1.pl [new file with mode: 0755]
Source/Test/filter_test_1.rc [new file with mode: 0644]
Source/Test/filter_test_2.pl [new file with mode: 0755]
Source/Test/filter_test_2.rc [new file with mode: 0644]
Source/Test/header_size_test.dat [new file with mode: 0644]
Source/Test/header_size_test.pl [new file with mode: 0755]
Source/Test/header_test.dat [new file with mode: 0644]
Source/Test/header_test.pl [new file with mode: 0755]
Source/Test/header_test2.dat [new file with mode: 0644]
Source/Test/header_test2.pl [new file with mode: 0755]
Source/Test/header_test3.dat [new file with mode: 0644]
Source/Test/header_test3.pl [new file with mode: 0755]
Source/Test/header_test4.dat [new file with mode: 0644]
Source/Test/header_test4.pl [new file with mode: 0755]
Source/Test/header_test5.dat [new file with mode: 0644]
Source/Test/header_test5.pl [new file with mode: 0755]
Source/Test/html_specialchar_test.dat [new file with mode: 0644]
Source/Test/html_specialchar_test.pl [new file with mode: 0755]
Source/Test/ignore_test.dat [new file with mode: 0644]
Source/Test/ignore_test.pl [new file with mode: 0755]
Source/Test/ignore_test.rc [new file with mode: 0644]
Source/Test/internalheader_test.dat [new file with mode: 0644]
Source/Test/internalheader_test.pl [new file with mode: 0755]
Source/Test/internalheader_test.refs [new file with mode: 0644]
Source/Test/internalheader_test2.pl [new file with mode: 0755]
Source/Test/item_test.dat [new file with mode: 0644]
Source/Test/item_test.pl [new file with mode: 0755]
Source/Test/latex_specialchar_test.dat [new file with mode: 0644]
Source/Test/latex_specialchar_test.pl [new file with mode: 0755]
Source/Test/link_test.dat [new file with mode: 0644]
Source/Test/link_test.pl [new file with mode: 0755]
Source/Test/lock_test.dat [new file with mode: 0755]
Source/Test/lock_test.pl [new file with mode: 0755]
Source/Test/makefile [new file with mode: 0644]
Source/Test/master_index_test.pl [new file with mode: 0755]
Source/Test/multidoc_test1.pl [new file with mode: 0755]
Source/Test/nosort_test.pl [new file with mode: 0755]
Source/Test/nosource_test.dat [new file with mode: 0644]
Source/Test/nosource_test.pl [new file with mode: 0755]
Source/Test/option_rc_test.dat [new file with mode: 0644]
Source/Test/option_rc_test.pl [new file with mode: 0755]
Source/Test/outputformat_test.dat [new file with mode: 0644]
Source/Test/outputformat_test.pl [new file with mode: 0755]
Source/Test/parameter_test.dat [new file with mode: 0644]
Source/Test/parameter_test.pl [new file with mode: 0755]
Source/Test/pipe_test.dat [new file with mode: 0644]
Source/Test/pipe_test.pl [new file with mode: 0755]
Source/Test/readme.txt [new file with mode: 0644]
Source/Test/remarkmarker_test.dat [new file with mode: 0644]
Source/Test/remarkmarker_test.pl [new file with mode: 0755]
Source/Test/robodoc.rc [new file with mode: 0644]
Source/Test/robohdrs_test.pl [new file with mode: 0644]
Source/Test/sort_test.pl [new file with mode: 0755]
Source/Test/tabtest.dat [new file with mode: 0644]
Source/Test/tabtest.pl [new file with mode: 0755]
Source/Test/url_test.dat [new file with mode: 0644]
Source/Test/url_test.pl [new file with mode: 0755]
Source/Test/user_css.css [new file with mode: 0644]
Source/Test/user_specified_css_file_test.pl [new file with mode: 0755]
Source/analyser.c [new file with mode: 0644]
Source/analyser.h [new file with mode: 0644]
Source/ascii_generator.c [new file with mode: 0644]
Source/ascii_generator.h [new file with mode: 0644]
Source/config.h.in [new file with mode: 0644]
Source/css_to_c.pl [new file with mode: 0644]
Source/directory.c [new file with mode: 0644]
Source/directory.h [new file with mode: 0644]
Source/document.c [new file with mode: 0644]
Source/document.h [new file with mode: 0644]
Source/file.c [new file with mode: 0644]
Source/file.h [new file with mode: 0644]
Source/generator.c [new file with mode: 0644]
Source/generator.h [new file with mode: 0644]
Source/globals.c [new file with mode: 0644]
Source/globals.h [new file with mode: 0644]
Source/headers.c [new file with mode: 0644]
Source/headers.h [new file with mode: 0644]
Source/headertypes.c [new file with mode: 0644]
Source/headertypes.h [new file with mode: 0644]
Source/html_generator.c [new file with mode: 0644]
Source/html_generator.h [new file with mode: 0644]
Source/items.c [new file with mode: 0644]
Source/items.h [new file with mode: 0644]
Source/latex_generator.c [new file with mode: 0644]
Source/latex_generator.h [new file with mode: 0644]
Source/links.c [new file with mode: 0644]
Source/links.h [new file with mode: 0644]
Source/lua_generator.c [new file with mode: 0644]
Source/lua_generator.h [new file with mode: 0644]
Source/makefile.am [new file with mode: 0644]
Source/makefile.bcc [new file with mode: 0644]
Source/makefile.mingw [new file with mode: 0644]
Source/makefile.mingw-cygwin [new file with mode: 0644]
Source/makefile.plain [new file with mode: 0644]
Source/makefile.win32 [new file with mode: 0644]
Source/makefile.wingcc [new file with mode: 0644]
Source/optioncheck.c [new file with mode: 0644]
Source/optioncheck.h [new file with mode: 0644]
Source/part.c [new file with mode: 0644]
Source/part.h [new file with mode: 0644]
Source/path.c [new file with mode: 0644]
Source/path.h [new file with mode: 0644]
Source/roboconfig.c [new file with mode: 0644]
Source/roboconfig.h [new file with mode: 0644]
Source/robodoc.c [new file with mode: 0644]
Source/robodoc.h [new file with mode: 0644]
Source/robodoc_basic_style.css [new file with mode: 0644]
Source/robohdrs.c [new file with mode: 0644]
Source/rtf_generator.c [new file with mode: 0644]
Source/rtf_generator.h [new file with mode: 0644]
Source/t/ROBOTestFrame.pm [new file with mode: 0644]
Source/t/Robodoc.pm [new file with mode: 0644]
Source/t/TestData/iso8859-1_german_header_c.xxd [new file with mode: 0644]
Source/t/TestData/iso8859-1_german_robodoc_rc.xxd [new file with mode: 0644]
Source/t/TestData/robodoc_unix_rc.xxd [new file with mode: 0644]
Source/t/TestData/robodoc_windows_rc.xxd [new file with mode: 0644]
Source/t/ascii.t [new file with mode: 0644]
Source/t/basics.t [new file with mode: 0644]
Source/t/encoding.t [new file with mode: 0644]
Source/t/errors.t [new file with mode: 0644]
Source/t/headers.t [new file with mode: 0644]
Source/t/items.t [new file with mode: 0644]
Source/t/latex.t [new file with mode: 0644]
Source/t/lib/IPC/Run.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/Debug.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/IO.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/Timer.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/Win32Helper.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/Win32IO.pm [new file with mode: 0644]
Source/t/lib/IPC/Run/Win32Pump.pm [new file with mode: 0644]
Source/t/lib/Test/File.pm [new file with mode: 0644]
Source/t/makefile [new file with mode: 0644]
Source/t/one_file_per_header.t [new file with mode: 0644]
Source/t/pipes.t [new file with mode: 0644]
Source/t/readme.txt [new file with mode: 0644]
Source/t/robodoc_rc.t [new file with mode: 0644]
Source/t/wiki_formatting.t [new file with mode: 0644]
Source/test_generator.c [new file with mode: 0644]
Source/test_generator.h [new file with mode: 0644]
Source/testheaders.c [new file with mode: 0644]
Source/todo.pl [new file with mode: 0644]
Source/troff_generator.c [new file with mode: 0644]
Source/troff_generator.h [new file with mode: 0644]
Source/unittest.c [new file with mode: 0644]
Source/unittest.h [new file with mode: 0644]
Source/util.c [new file with mode: 0644]
Source/util.h [new file with mode: 0644]
Source/xmldocbook_generator.c [new file with mode: 0644]
Source/xmldocbook_generator.h [new file with mode: 0644]
TODO [new file with mode: 0644]
UPGRADE [new file with mode: 0644]
Win32/robodoc.dsp [new file with mode: 0644]
Win32/robodoc.dsw [new file with mode: 0644]
Win32/robodoc.sln [new file with mode: 0644]
Win32/robodoc.vcproj [new file with mode: 0644]
bump.pl [new file with mode: 0755]
configure.in [new file with mode: 0644]
do.sh [new file with mode: 0755]
dos2unix.pl [new file with mode: 0644]
makefile.am [new file with mode: 0644]
osx/License.rtf.in [new file with mode: 0644]
osx/ReadMe.rtf.in [new file with mode: 0644]
osx/Welcome.rtf [new file with mode: 0644]
osx/dmg.sh [new file with mode: 0755]
robodoc.spec.in [new file with mode: 0644]
rpm.mk [new file with mode: 0644]
todo.pl [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..140e293
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,23 @@
+# 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
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/CVS.adm/MstData.stp b/CVS.adm/MstData.stp
new file mode 100644 (file)
index 0000000..2572ee1
--- /dev/null
@@ -0,0 +1,4 @@
+<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>
diff --git a/CVS.adm/UsgData.stp b/CVS.adm/UsgData.stp
new file mode 100644 (file)
index 0000000..1071ef3
--- /dev/null
@@ -0,0 +1,60 @@
+<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>
diff --git a/CVS.adm/prjprm.stp b/CVS.adm/prjprm.stp
new file mode 100644 (file)
index 0000000..1b0d3cb
--- /dev/null
@@ -0,0 +1,20 @@
+<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>
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..08de285
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,710 @@
+# 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 &gt and &lt 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.
+
+
diff --git a/Contributions/robodoc.kaptn b/Contributions/robodoc.kaptn
new file mode 100644 (file)
index 0000000..f1d639b
--- /dev/null
@@ -0,0 +1,292 @@
+#!/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" | @;
+#############################################################
+
diff --git a/Contributions/robodoc2pod.pl b/Contributions/robodoc2pod.pl
new file mode 100644 (file)
index 0000000..b88f4e1
--- /dev/null
@@ -0,0 +1,162 @@
+#!/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";
+
diff --git a/DEVELOPERS b/DEVELOPERS
new file mode 100644 (file)
index 0000000..cc698fd
--- /dev/null
@@ -0,0 +1,46 @@
+$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 )
+
diff --git a/Design/design.txt b/Design/design.txt
new file mode 100644 (file)
index 0000000..b019b97
--- /dev/null
@@ -0,0 +1,198 @@
+
+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.
+
+
diff --git a/Design/uml.fig b/Design/uml.fig
new file mode 100644 (file)
index 0000000..53f8dbb
--- /dev/null
@@ -0,0 +1,130 @@
+#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
diff --git a/Docs/bugs.xml b/Docs/bugs.xml
new file mode 100644 (file)
index 0000000..814e322
--- /dev/null
@@ -0,0 +1,8 @@
+
+<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>
+
diff --git a/Docs/configuration.xml b/Docs/configuration.xml
new file mode 100644 (file)
index 0000000..efb60fe
--- /dev/null
@@ -0,0 +1,464 @@
+<!-- $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>
+
diff --git a/Docs/docbook-simple/sdocbook.dtd b/Docs/docbook-simple/sdocbook.dtd
new file mode 100644 (file)
index 0000000..b7b016d
--- /dev/null
@@ -0,0 +1,1886 @@
+<!-- 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           "&#x000C1;" >
+<!ENTITY aacute           "&#x000E1;" >
+<!ENTITY Acirc            "&#x000C2;" >
+<!ENTITY acirc            "&#x000E2;" >
+<!ENTITY AElig            "&#x000C6;" >
+<!ENTITY aelig            "&#x000E6;" >
+<!ENTITY Agrave           "&#x000C0;" >
+<!ENTITY agrave           "&#x000E0;" >
+<!ENTITY Aring            "&#x000C5;" >
+<!ENTITY aring            "&#x000E5;" >
+<!ENTITY Atilde           "&#x000C3;" >
+<!ENTITY atilde           "&#x000E3;" >
+<!ENTITY Auml             "&#x000C4;" >
+<!ENTITY auml             "&#x000E4;" >
+<!ENTITY Ccedil           "&#x000C7;" >
+<!ENTITY ccedil           "&#x000E7;" >
+<!ENTITY Eacute           "&#x000C9;" >
+<!ENTITY eacute           "&#x000E9;" >
+<!ENTITY Ecirc            "&#x000CA;" >
+<!ENTITY ecirc            "&#x000EA;" >
+<!ENTITY Egrave           "&#x000C8;" >
+<!ENTITY egrave           "&#x000E8;" >
+<!ENTITY ETH              "&#x000D0;" >
+<!ENTITY eth              "&#x000F0;" >
+<!ENTITY Euml             "&#x000CB;" >
+<!ENTITY euml             "&#x000EB;" >
+<!ENTITY Iacute           "&#x000CD;" >
+<!ENTITY iacute           "&#x000ED;" >
+<!ENTITY Icirc            "&#x000CE;" >
+<!ENTITY icirc            "&#x000EE;" >
+<!ENTITY Igrave           "&#x000CC;" >
+<!ENTITY igrave           "&#x000EC;" >
+<!ENTITY Iuml             "&#x000CF;" >
+<!ENTITY iuml             "&#x000EF;" >
+<!ENTITY Ntilde           "&#x000D1;" >
+<!ENTITY ntilde           "&#x000F1;" >
+<!ENTITY Oacute           "&#x000D3;" >
+<!ENTITY oacute           "&#x000F3;" >
+<!ENTITY Ocirc            "&#x000D4;" >
+<!ENTITY ocirc            "&#x000F4;" >
+<!ENTITY Ograve           "&#x000D2;" >
+<!ENTITY ograve           "&#x000F2;" >
+<!ENTITY Oslash           "&#x000D8;" >
+<!ENTITY oslash           "&#x000F8;" >
+<!ENTITY Otilde           "&#x000D5;" >
+<!ENTITY otilde           "&#x000F5;" >
+<!ENTITY Ouml             "&#x000D6;" >
+<!ENTITY ouml             "&#x000F6;" >
+<!ENTITY szlig            "&#x000DF;" >
+<!ENTITY THORN            "&#x000DE;" >
+<!ENTITY thorn            "&#x000FE;" >
+<!ENTITY Uacute           "&#x000DA;" >
+<!ENTITY uacute           "&#x000FA;" >
+<!ENTITY Ucirc            "&#x000DB;" >
+<!ENTITY ucirc            "&#x000FB;" >
+<!ENTITY Ugrave           "&#x000D9;" >
+<!ENTITY ugrave           "&#x000F9;" >
+<!ENTITY Uuml             "&#x000DC;" >
+<!ENTITY uuml             "&#x000FC;" >
+<!ENTITY Yacute           "&#x000DD;" >
+<!ENTITY yacute           "&#x000FD;" >
+<!ENTITY yuml             "&#x000FF;" >
+<!ENTITY amp              "&#38;#38;" >
+<!ENTITY apos             "&#x00027;" >
+<!ENTITY ast              "&#x0002A;" >
+<!ENTITY brvbar           "&#x000A6;" >
+<!ENTITY bsol             "&#x0005C;" >
+<!ENTITY cent             "&#x000A2;" >
+<!ENTITY colon            "&#x0003A;" >
+<!ENTITY comma            "&#x0002C;" >
+<!ENTITY commat           "&#x00040;" >
+<!ENTITY copy             "&#x000A9;" >
+<!ENTITY curren           "&#x000A4;" >
+<!ENTITY darr             "&#x02193;" >
+<!ENTITY deg              "&#x000B0;" >
+<!ENTITY divide           "&#x000F7;" >
+<!ENTITY dollar           "&#x00024;" >
+<!ENTITY equals           "&#x0003D;" >
+<!ENTITY excl             "&#x00021;" >
+<!ENTITY frac12           "&#x000BD;" >
+<!ENTITY frac14           "&#x000BC;" >
+<!ENTITY frac18           "&#x0215B;" >
+<!ENTITY frac34           "&#x000BE;" >
+<!ENTITY frac38           "&#x0215C;" >
+<!ENTITY frac58           "&#x0215D;" >
+<!ENTITY frac78           "&#x0215E;" >
+<!ENTITY gt               "&#x0003E;" >
+<!ENTITY half             "&#x000BD;" >
+<!ENTITY horbar           "&#x02015;" >
+<!ENTITY hyphen           "&#x02010;" >
+<!ENTITY iexcl            "&#x000A1;" >
+<!ENTITY iquest           "&#x000BF;" >
+<!ENTITY laquo            "&#x000AB;" >
+<!ENTITY larr             "&#x02190;" >
+<!ENTITY lcub             "&#x0007B;" >
+<!ENTITY ldquo            "&#x0201C;" >
+<!ENTITY lowbar           "&#x0005F;" >
+<!ENTITY lpar             "&#x00028;" >
+<!ENTITY lsqb             "&#x0005B;" >
+<!ENTITY lsquo            "&#x02018;" >
+<!ENTITY lt               "&#38;#60;" >
+<!ENTITY micro            "&#x000B5;" >
+<!ENTITY middot           "&#x000B7;" >
+<!ENTITY nbsp             "&#x000A0;" >
+<!ENTITY not              "&#x000AC;" >
+<!ENTITY num              "&#x00023;" >
+<!ENTITY ohm              "&#x02126;" >
+<!ENTITY ordf             "&#x000AA;" >
+<!ENTITY ordm             "&#x000BA;" >
+<!ENTITY para             "&#x000B6;" >
+<!ENTITY percnt           "&#x00025;" >
+<!ENTITY period           "&#x0002E;" >
+<!ENTITY plus             "&#x0002B;" >
+<!ENTITY plusmn           "&#x000B1;" >
+<!ENTITY pound            "&#x000A3;" >
+<!ENTITY quest            "&#x0003F;" >
+<!ENTITY quot             "&#x00022;" >
+<!ENTITY raquo            "&#x000BB;" >
+<!ENTITY rarr             "&#x02192;" >
+<!ENTITY rcub             "&#x0007D;" >
+<!ENTITY rdquo            "&#x0201D;" >
+<!ENTITY reg              "&#x000AE;" >
+<!ENTITY rpar             "&#x00029;" >
+<!ENTITY rsqb             "&#x0005D;" >
+<!ENTITY rsquo            "&#x02019;" >
+<!ENTITY sect             "&#x000A7;" >
+<!ENTITY semi             "&#x0003B;" >
+<!ENTITY shy              "&#x000AD;" >
+<!ENTITY sol              "&#x0002F;" >
+<!ENTITY sung             "&#x0266A;" >
+<!ENTITY sup1             "&#x000B9;" >
+<!ENTITY sup2             "&#x000B2;" >
+<!ENTITY sup3             "&#x000B3;" >
+<!ENTITY times            "&#x000D7;" >
+<!ENTITY trade            "&#x02122;" >
+<!ENTITY uarr             "&#x02191;" >
+<!ENTITY verbar           "&#x0007C;" >
+<!ENTITY yen              "&#x000A5;" >
+<!ENTITY blank            "&#x02423;" >
+<!ENTITY blk12            "&#x02592;" >
+<!ENTITY blk14            "&#x02591;" >
+<!ENTITY blk34            "&#x02593;" >
+<!ENTITY block            "&#x02588;" >
+<!ENTITY bull             "&#x02022;" >
+<!ENTITY caret            "&#x02041;" >
+<!ENTITY check            "&#x02713;" >
+<!ENTITY cir              "&#x025CB;" >
+<!ENTITY clubs            "&#x02663;" >
+<!ENTITY copysr           "&#x02117;" >
+<!ENTITY cross            "&#x02717;" >
+<!ENTITY Dagger           "&#x02021;" >
+<!ENTITY dagger           "&#x02020;" >
+<!ENTITY dash             "&#x02010;" >
+<!ENTITY diams            "&#x02666;" >
+<!ENTITY dlcrop           "&#x0230D;" >
+<!ENTITY drcrop           "&#x0230C;" >
+<!ENTITY dtri             "&#x025BF;" >
+<!ENTITY dtrif            "&#x025BE;" >
+<!ENTITY emsp             "&#x02003;" >
+<!ENTITY emsp13           "&#x02004;" >
+<!ENTITY emsp14           "&#x02005;" >
+<!ENTITY ensp             "&#x02002;" >
+<!ENTITY female           "&#x02640;" >
+<!ENTITY ffilig           "&#x0FB03;" >
+<!ENTITY fflig            "&#x0FB00;" >
+<!ENTITY ffllig           "&#x0FB04;" >
+<!ENTITY filig            "&#x0FB01;" >
+<!ENTITY flat             "&#x0266D;" >
+<!ENTITY fllig            "&#x0FB02;" >
+<!ENTITY frac13           "&#x02153;" >
+<!ENTITY frac15           "&#x02155;" >
+<!ENTITY frac16           "&#x02159;" >
+<!ENTITY frac23           "&#x02154;" >
+<!ENTITY frac25           "&#x02156;" >
+<!ENTITY frac35           "&#x02157;" >
+<!ENTITY frac45           "&#x02158;" >
+<!ENTITY frac56           "&#x0215A;" >
+<!ENTITY hairsp           "&#x0200A;" >
+<!ENTITY hearts           "&#x02665;" >
+<!ENTITY hellip           "&#x02026;" >
+<!ENTITY hybull           "&#x02043;" >
+<!ENTITY incare           "&#x02105;" >
+<!ENTITY ldquor           "&#x0201E;" >
+<!ENTITY lhblk            "&#x02584;" >
+<!ENTITY loz              "&#x025CA;" >
+<!ENTITY lozf             "&#x029EB;" >
+<!ENTITY lsquor           "&#x0201A;" >
+<!ENTITY ltri             "&#x025C3;" >
+<!ENTITY ltrif            "&#x025C2;" >
+<!ENTITY male             "&#x02642;" >
+<!ENTITY malt             "&#x02720;" >
+<!ENTITY marker           "&#x025AE;" >
+<!ENTITY mdash            "&#x02014;" >
+<!ENTITY mldr             "&#x02026;" >
+<!ENTITY natur            "&#x0266E;" >
+<!ENTITY ndash            "&#x02013;" >
+<!ENTITY nldr             "&#x02025;" >
+<!ENTITY numsp            "&#x02007;" >
+<!ENTITY phone            "&#x0260E;" >
+<!ENTITY puncsp           "&#x02008;" >
+<!ENTITY rdquor           "&#x0201D;" >
+<!ENTITY rect             "&#x025AD;" >
+<!ENTITY rsquor           "&#x02019;" >
+<!ENTITY rtri             "&#x025B9;" >
+<!ENTITY rtrif            "&#x025B8;" >
+<!ENTITY rx               "&#x0211E;" >
+<!ENTITY sext             "&#x02736;" >
+<!ENTITY sharp            "&#x0266F;" >
+<!ENTITY spades           "&#x02660;" >
+<!ENTITY squ              "&#x025A1;" >
+<!ENTITY squf             "&#x025AA;" >
+<!ENTITY star             "&#x02606;" >
+<!ENTITY starf            "&#x02605;" >
+<!ENTITY target           "&#x02316;" >
+<!ENTITY telrec           "&#x02315;" >
+<!ENTITY thinsp           "&#x02009;" >
+<!ENTITY uhblk            "&#x02580;" >
+<!ENTITY ulcrop           "&#x0230F;" >
+<!ENTITY urcrop           "&#x0230E;" >
+<!ENTITY utri             "&#x025B5;" >
+<!ENTITY utrif            "&#x025B4;" >
+<!ENTITY vellip           "&#x022EE;" >
+<!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 "&#x20AC;">
+<!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;)*
+               >
diff --git a/Docs/example_makefile b/Docs/example_makefile
new file mode 100644 (file)
index 0000000..8d6459d
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# OBSOLETE
+#
+
diff --git a/Docs/examples.xml b/Docs/examples.xml
new file mode 100644 (file)
index 0000000..15005f6
--- /dev/null
@@ -0,0 +1,164 @@
+<!-- $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>
+
diff --git a/Docs/extracting.xml b/Docs/extracting.xml
new file mode 100644 (file)
index 0000000..25b66ad
--- /dev/null
@@ -0,0 +1,121 @@
+<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>
+
diff --git a/Docs/faq.dat b/Docs/faq.dat
new file mode 100644 (file)
index 0000000..73ff6d7
--- /dev/null
@@ -0,0 +1,73 @@
+<!-- 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
+
diff --git a/Docs/faqfooter.html b/Docs/faqfooter.html
new file mode 100644 (file)
index 0000000..b15a54b
--- /dev/null
@@ -0,0 +1,9 @@
+<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>
diff --git a/Docs/faqheader.html b/Docs/faqheader.html
new file mode 100644 (file)
index 0000000..3f9ab82
--- /dev/null
@@ -0,0 +1,25 @@
+<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>
diff --git a/Docs/header_specs.pl b/Docs/header_specs.pl
new file mode 100644 (file)
index 0000000..53e08a3
--- /dev/null
@@ -0,0 +1,203 @@
+#!/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;
+
diff --git a/Docs/installing.xml b/Docs/installing.xml
new file mode 100644 (file)
index 0000000..58972ab
--- /dev/null
@@ -0,0 +1,33 @@
+<?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>
diff --git a/Docs/main.css b/Docs/main.css
new file mode 100644 (file)
index 0000000..5a5c7a0
--- /dev/null
@@ -0,0 +1,139 @@
+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;  
+ }
diff --git a/Docs/makefile.am b/Docs/makefile.am
new file mode 100644 (file)
index 0000000..e2515f5
--- /dev/null
@@ -0,0 +1,25 @@
+## 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
+#
diff --git a/Docs/makefile.cygwin b/Docs/makefile.cygwin
new file mode 100644 (file)
index 0000000..f9bbc92
--- /dev/null
@@ -0,0 +1,38 @@
+# 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
diff --git a/Docs/makefile.plain b/Docs/makefile.plain
new file mode 100644 (file)
index 0000000..1dbecde
--- /dev/null
@@ -0,0 +1,19 @@
+# $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
diff --git a/Docs/makefile.win32 b/Docs/makefile.win32
new file mode 100644 (file)
index 0000000..d3de843
--- /dev/null
@@ -0,0 +1,30 @@
+# $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
diff --git a/Docs/manual-cygwin.xsl b/Docs/manual-cygwin.xsl
new file mode 100644 (file)
index 0000000..b0aba10
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>
+
diff --git a/Docs/manual.css b/Docs/manual.css
new file mode 100644 (file)
index 0000000..a591c71
--- /dev/null
@@ -0,0 +1,57 @@
+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;
+}
+
diff --git a/Docs/manual.html b/Docs/manual.html
new file mode 100644 (file)
index 0000000..8af0ffa
--- /dev/null
@@ -0,0 +1,1690 @@
+<?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.&#xA0;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.&#xA0;header separate characters block">header separate characters block</a>.
+    See <a href="#customizing" title="7.&#xA0;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.&#xA0;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.&#xA0;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) &lt;year&gt;-&lt;year&gt; by
+                    &lt;company/person&gt;"
+                </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.&#xA0;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 &lt;H1&gt;,  &lt;H2&gt;,  &lt;H3&gt; 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">
+    &lt;p&gt;This is some example text.
+    And some more.&lt;/p&gt;
+
+    &lt;p&gt;This is even more, and we start a list:&lt;/p&gt;
+    &lt;ul&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;/ul&gt;
+
+    &lt;p&gt;And we can also do preformatted stuff
+    by indenting&lt;/p&gt;
+    &lt;pre&gt;
+        +--------+
+        |        |
+        +--------+
+    &lt;/pre&gt;
+    &lt;p&gt; The box will stay.&lt;/p&gt;
+</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 &gt; api.html
+</pre><p>
+    Where <code class="filename">api.xsl</code> contains:
+</p><pre class="literallayout">
+&lt;?xml version='1.0'?&gt;
+&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&gt;
+    &lt;xsl:import href="e:/docbook/html/docbook.xsl"/&gt;
+    &lt;xsl:param name="admon.graphics" select="1"/&gt;
+    &lt;xsl:param name="section.autolabel" select="1"/&gt;
+&lt;/xsl:stylesheet&gt;
+
+</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">
+&lt;?xml version='1.0'?&gt;
+&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&gt;
+    &lt;xsl:import href="e:/docbook/htmlhelp/htmlhelp.xsl"/&gt;
+    &lt;xsl:param name="admon.graphics" select="1"/&gt;
+    &lt;xsl:param name="section.autolabel" select="1"/&gt;
+&lt;/xsl:stylesheet&gt;
+
+</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">&lt;!DOCTYPE&gt;</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">
+&lt;!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+"docbook-simple/sdocbook.dtd"&gt;
+</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">&lt;html&gt;&lt;head&gt; ..... &lt;body&gt;</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">&lt;!DOCTYPE&gt;</code>,
+                    <code class="literal">&lt;article&gt;</code>, and
+                    <code class="literal">&lt;articleinfo&gt;</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">&lt;/body&gt;&lt;/html&gt;</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">&lt;/article&gt;</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">&lt;PRE&gt;</code> and
+                    <code class="literal">&lt;/PRE&gt;</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.&#xA0;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.&#xA0;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.&#xA0; 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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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.&#xA0;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 &lt;
+    is translated to a &amp;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 &lt;CENTER&gt;This will be included in &lt;B&gt;HTML&lt;/B&gt; output.&lt;/CENTER&gt;
+ *   |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&lt;B&gt;Moi!&lt;/B&gt;
+ *   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} &amp; = &amp; 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} &amp; = &amp; \hat{u}-v \; ,
+ *    |latex \label{diffspot:2} \\
+ *    |latex \frac{\partial r}{\partial \tau} &amp; = &amp; D_r {\nabla}^2 r \; .
+ *    |latex \label{diffspAot:3}
+ *    |latex \end{eqnarray}
+ *    |html &lt;I&gt;TODO: write this in html&lt;/I&gt;
+ *   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">&lt;a href="body"&gt;body&lt;/A&gt;</code>
+    </p></li><li><p><code class="literal">file:/body</code> -- This is replaced with
+    <code class="literal">&lt;a href="file:/body"&gt;file:/body&lt;/A&gt;</code>
+    </p></li><li><p><code class="literal">mailto:body</code> -- This is replaced with
+    <code class="literal">&lt;a href="mailto:body"&gt;body&lt;/A&gt;</code>
+    </p></li><li><p><code class="literal">http://body</code> -- This is replaced with
+    <code class="literal">&lt;a href="http://body"&gt;http://body&lt;/A&gt;</code>
+    </p></li><li><p><code class="literal">image:body</code> -- This is replaced with
+    <code class="literal">&lt;image src="body"&gt;</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 &gt; 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." &gt; 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 = "&lt;f0&gt; |&lt;f1&gt; |&lt;f2&gt; |&lt;f3&gt; |&lt;f4&gt; |&lt;f5&gt; |&lt;f6&gt; | ",height=2.0];
+ *   node [width = 1.5];
+ *   node1 [label = "{&lt;n&gt; n14 | 719 |&lt;p&gt; }"];
+ *   node2 [label = "{&lt;n&gt; a1  | 805 |&lt;p&gt; }"];
+ *   node3 [label = "{&lt;n&gt; i9  | 718 |&lt;p&gt; }"];
+ *   node4 [label = "{&lt;n&gt; e5  | 989 |&lt;p&gt; }"];
+ *   node5 [label = "{&lt;n&gt; t20 | 959 |&lt;p&gt; }"] ;
+ *   node6 [label = "{&lt;n&gt; o15 | 794 |&lt;p&gt; }"] ;
+ *   node7 [label = "{&lt;n&gt; s19 | 659 |&lt;p&gt; }"] ;
+ *
+ *   node0:f0 -&gt; node1:n;
+ *   node0:f1 -&gt; node2:n;
+ *   node0:f2 -&gt; node3:n;
+ *   node0:f5 -&gt; node4:n;
+ *   node0:f6 -&gt; node5:n;
+ *   node2:p -&gt; node6:n;
+ *   node4:p -&gt; 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-&gt;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>
diff --git a/Docs/manual.xml b/Docs/manual.xml
new file mode 100644 (file)
index 0000000..6259c7f
--- /dev/null
@@ -0,0 +1,110 @@
+<!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>
+
diff --git a/Docs/manual.xsl b/Docs/manual.xsl
new file mode 100644 (file)
index 0000000..6e4a004
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>
+
diff --git a/Docs/manual_html_help-cygwin.xsl b/Docs/manual_html_help-cygwin.xsl
new file mode 100644 (file)
index 0000000..4d64fd5
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
+
diff --git a/Docs/manual_html_help.xsl b/Docs/manual_html_help.xsl
new file mode 100644 (file)
index 0000000..5a88fe9
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
+
diff --git a/Docs/manual_html_help_win32.xsl b/Docs/manual_html_help_win32.xsl
new file mode 100644 (file)
index 0000000..6c53693
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
+
diff --git a/Docs/manual_win32.xsl b/Docs/manual_win32.xsl
new file mode 100644 (file)
index 0000000..203bb81
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>
+
diff --git a/Docs/options.xml b/Docs/options.xml
new file mode 100644 (file)
index 0000000..36130e7
--- /dev/null
@@ -0,0 +1,400 @@
+
+
+
+<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>&lt;!DOCTYPE&gt;</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>
+&lt;!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+"docbook-simple/sdocbook.dtd"&gt;
+</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>&lt;html&gt;&lt;head&gt; ..... &lt;body&gt;</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>&lt;!DOCTYPE&gt;</literal>,
+                    <literal>&lt;article&gt;</literal>, and
+                    <literal>&lt;articleinfo&gt;</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>&lt;/body&gt;&lt;/html&gt;</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>&lt;/article&gt;</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>&lt;PRE&gt;</literal> and
+                    <literal>&lt;/PRE&gt;</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>
+
+
diff --git a/Docs/preface.xml b/Docs/preface.xml
new file mode 100644 (file)
index 0000000..d447ef8
--- /dev/null
@@ -0,0 +1,46 @@
+<!-- $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>
+
diff --git a/Docs/preparing.xml b/Docs/preparing.xml
new file mode 100644 (file)
index 0000000..3b47f03
--- /dev/null
@@ -0,0 +1,784 @@
+<!-- $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) &lt;year&gt;-&lt;year&gt; by
+                    &lt;company/person&gt;"
+                </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 &lt;H1&gt;,  &lt;H2&gt;,  &lt;H3&gt; 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">
+    &lt;p&gt;This is some example text.
+    And some more.&lt;/p&gt;
+
+    &lt;p&gt;This is even more, and we start a list:&lt;/p&gt;
+    &lt;ul&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;li&gt;a list item&lt;/li&gt;
+    &lt;/ul&gt;
+
+    &lt;p&gt;And we can also do preformatted stuff
+    by indenting&lt;/p&gt;
+    &lt;pre&gt;
+        +--------+
+        |        |
+        +--------+
+    &lt;/pre&gt;
+    &lt;p&gt; The box will stay.&lt;/p&gt;
+</literallayout>
+</example>
+
+</section>
+
+</section>
+
+
diff --git a/Docs/robodoc.1 b/Docs/robodoc.1
new file mode 100644 (file)
index 0000000..8ce7dc5
--- /dev/null
@@ -0,0 +1,188 @@
+.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)
diff --git a/Docs/robodoc.1.in b/Docs/robodoc.1.in
new file mode 100644 (file)
index 0000000..dab6510
--- /dev/null
@@ -0,0 +1,188 @@
+.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)
diff --git a/Docs/robodoc.rc b/Docs/robodoc.rc
new file mode 100644 (file)
index 0000000..5a89f80
--- /dev/null
@@ -0,0 +1,113 @@
+#
+# $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:
+    */
+    #
+
diff --git a/Docs/robohdrs.1 b/Docs/robohdrs.1
new file mode 100644 (file)
index 0000000..7e4394c
--- /dev/null
@@ -0,0 +1,80 @@
+.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)
+
+
+
diff --git a/Docs/tips.xml b/Docs/tips.xml
new file mode 100644 (file)
index 0000000..79d23d3
--- /dev/null
@@ -0,0 +1,436 @@
+<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 &lt;
+    is translated to a &amp;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 &lt;CENTER&gt;This will be included in &lt;B&gt;HTML&lt;/B&gt; output.&lt;/CENTER&gt;
+ *   |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&lt;B&gt;Moi!&lt;/B&gt;
+ *   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} &amp; = &amp; 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} &amp; = &amp; \hat{u}-v \; ,
+ *    |latex \label{diffspot:2} \\
+ *    |latex \frac{\partial r}{\partial \tau} &amp; = &amp; D_r {\nabla}^2 r \; .
+ *    |latex \label{diffspAot:3}
+ *    |latex \end{eqnarray}
+ *    |html &lt;I&gt;TODO: write this in html&lt;/I&gt;
+ *   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>&lt;a href="body"&gt;body&lt;/A&gt;</literal>
+    </para></listitem>
+
+    <listitem><para><literal>file:/body</literal> -- This is replaced with
+    <literal>&lt;a href="file:/body"&gt;file:/body&lt;/A&gt;</literal>
+    </para></listitem>
+
+    <listitem><para><literal>mailto:body</literal> -- This is replaced with
+    <literal>&lt;a href="mailto:body"&gt;body&lt;/A&gt;</literal>
+    </para></listitem>
+
+    <listitem><para><literal>http://body</literal> -- This is replaced with
+    <literal>&lt;a href="http://body"&gt;http://body&lt;/A&gt;</literal>
+    </para></listitem>
+
+    <listitem><para><literal>image:body</literal> -- This is replaced with
+    <literal>&lt;image src="body"&gt;</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 &gt; 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." &gt; 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 = "&lt;f0&gt; |&lt;f1&gt; |&lt;f2&gt; |&lt;f3&gt; |&lt;f4&gt; |&lt;f5&gt; |&lt;f6&gt; | ",height=2.0];
+ *   node [width = 1.5];
+ *   node1 [label = "{&lt;n&gt; n14 | 719 |&lt;p&gt; }"];
+ *   node2 [label = "{&lt;n&gt; a1  | 805 |&lt;p&gt; }"];
+ *   node3 [label = "{&lt;n&gt; i9  | 718 |&lt;p&gt; }"];
+ *   node4 [label = "{&lt;n&gt; e5  | 989 |&lt;p&gt; }"];
+ *   node5 [label = "{&lt;n&gt; t20 | 959 |&lt;p&gt; }"] ;
+ *   node6 [label = "{&lt;n&gt; o15 | 794 |&lt;p&gt; }"] ;
+ *   node7 [label = "{&lt;n&gt; s19 | 659 |&lt;p&gt; }"] ;
+ *
+ *   node0:f0 -&gt; node1:n;
+ *   node0:f1 -&gt; node2:n;
+ *   node0:f2 -&gt; node3:n;
+ *   node0:f5 -&gt; node4:n;
+ *   node0:f6 -&gt; node5:n;
+ *   node2:p -&gt; node6:n;
+ *   node4:p -&gt; 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>
+
diff --git a/Examples/PerlExample/Doc/robo_classes.html b/Examples/PerlExample/Doc/robo_classes.html
new file mode 100644 (file)
index 0000000..9d894c8
--- /dev/null
@@ -0,0 +1,40 @@
+<!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>
diff --git a/Examples/PerlExample/Doc/robo_methods.html b/Examples/PerlExample/Doc/robo_methods.html
new file mode 100644 (file)
index 0000000..9053c84
--- /dev/null
@@ -0,0 +1,40 @@
+<!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>
diff --git a/Examples/PerlExample/Doc/robo_modules.html b/Examples/PerlExample/Doc/robo_modules.html
new file mode 100644 (file)
index 0000000..2d523f0
--- /dev/null
@@ -0,0 +1,37 @@
+<!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>
diff --git a/Examples/PerlExample/Doc/robo_sourcefiles.html b/Examples/PerlExample/Doc/robo_sourcefiles.html
new file mode 100644 (file)
index 0000000..c216b60
--- /dev/null
@@ -0,0 +1,52 @@
+<!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>
diff --git a/Examples/PerlExample/Source/Box.pm b/Examples/PerlExample/Source/Box.pm
new file mode 100644 (file)
index 0000000..3677aac
--- /dev/null
@@ -0,0 +1,24 @@
+#!/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;
+
diff --git a/Examples/PerlExample/Source/Box/RectangularBox.pm b/Examples/PerlExample/Source/Box/RectangularBox.pm
new file mode 100644 (file)
index 0000000..c04a6d1
--- /dev/null
@@ -0,0 +1,47 @@
+#!/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;
+
+
diff --git a/Examples/PerlExample/Source/Box/SquareBox.pm b/Examples/PerlExample/Source/Box/SquareBox.pm
new file mode 100644 (file)
index 0000000..902ecfd
--- /dev/null
@@ -0,0 +1,65 @@
+#!/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;
+
diff --git a/Examples/PerlExample/Source/Cargo.txt b/Examples/PerlExample/Source/Cargo.txt
new file mode 100644 (file)
index 0000000..b20e97f
--- /dev/null
@@ -0,0 +1,9 @@
+
+#****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.
+#
+#****
diff --git a/Examples/PerlExample/Source/Loader.txt b/Examples/PerlExample/Source/Loader.txt
new file mode 100644 (file)
index 0000000..20249d3
--- /dev/null
@@ -0,0 +1,9 @@
+#****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.
+#
+#****
+
diff --git a/Examples/PerlExample/Source/SmartLoader.pm b/Examples/PerlExample/Source/SmartLoader.pm
new file mode 100644 (file)
index 0000000..f1ca4ce
--- /dev/null
@@ -0,0 +1,34 @@
+#!/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;
diff --git a/Examples/PerlExample/Source/TruckPacker.pl b/Examples/PerlExample/Source/TruckPacker.pl
new file mode 100644 (file)
index 0000000..ac635a8
--- /dev/null
@@ -0,0 +1,25 @@
+#!/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();
+
+
diff --git a/Examples/PerlExample/robodoc.rc b/Examples/PerlExample/robodoc.rc
new file mode 100644 (file)
index 0000000..4a8532d
--- /dev/null
@@ -0,0 +1,11 @@
+options:
+    --src ./Source
+    --doc ./Doc
+    --html
+    --multidoc
+    --sections
+    --tell
+    --toc
+    --index
+ignore files:
+    CVS
diff --git a/Headers/assembler.sample b/Headers/assembler.sample
new file mode 100644 (file)
index 0000000..dbf066b
--- /dev/null
@@ -0,0 +1,63 @@
+****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
+*
+**********
diff --git a/Headers/basic.sample b/Headers/basic.sample
new file mode 100644 (file)
index 0000000..22fca0c
--- /dev/null
@@ -0,0 +1,63 @@
+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  ********
diff --git a/Headers/c.sample b/Headers/c.sample
new file mode 100644 (file)
index 0000000..292710d
--- /dev/null
@@ -0,0 +1,46 @@
+/****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
+**********
+*/
+
diff --git a/Headers/cpp.sample b/Headers/cpp.sample
new file mode 100644 (file)
index 0000000..1a64a60
--- /dev/null
@@ -0,0 +1,68 @@
+/****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
+**********
+*/
+
diff --git a/Headers/forth.sample b/Headers/forth.sample
new file mode 100644 (file)
index 0000000..14c1c6e
--- /dev/null
@@ -0,0 +1,43 @@
+\ ****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()
+
+\ ********
diff --git a/Headers/fortran.sample b/Headers/fortran.sample
new file mode 100644 (file)
index 0000000..b646a73
--- /dev/null
@@ -0,0 +1,63 @@
+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    ********
diff --git a/Headers/html.sample b/Headers/html.sample
new file mode 100644 (file)
index 0000000..1d6d7ba
--- /dev/null
@@ -0,0 +1,38 @@
+<!---****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>
+
+<!---********---->
diff --git a/Headers/makefile b/Headers/makefile
new file mode 100644 (file)
index 0000000..8d25b83
--- /dev/null
@@ -0,0 +1,2 @@
+clean:
+       rm -f *~
diff --git a/Headers/tcl.sample b/Headers/tcl.sample
new file mode 100644 (file)
index 0000000..f3551f5
--- /dev/null
@@ -0,0 +1,39 @@
+#****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()
+
+#********
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..095b1eb
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,231 @@
+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.
+
diff --git a/INSTALL.Debian b/INSTALL.Debian
new file mode 100644 (file)
index 0000000..ddd8f09
--- /dev/null
@@ -0,0 +1,4 @@
+Debian users can install ROBODoc using apt-get as follows:
+
+       apt-get install robodoc
+
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..1c53bca
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,3 @@
+See the changelog for a detailed list of add new
+features.
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..7a921bb
--- /dev/null
+++ b/README
@@ -0,0 +1,51 @@
+$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 
+
+
+
diff --git a/Source/.indent.pro b/Source/.indent.pro
new file mode 100644 (file)
index 0000000..b93cb5d
--- /dev/null
@@ -0,0 +1,13 @@
+-nut
+-i 4
+-bli 0
+-bls
+-bad
+-npcs
+-bfda
+-di20
+-prs
+-npsl
+--verbose
+-T FILE
+-T actions_t
diff --git a/Source/Test/FileFilterSrc/README b/Source/Test/FileFilterSrc/README
new file mode 100644 (file)
index 0000000..b532f3d
--- /dev/null
@@ -0,0 +1 @@
+For testing
diff --git a/Source/Test/FileFilterSrc/test.pas b/Source/Test/FileFilterSrc/test.pas
new file mode 100644 (file)
index 0000000..a561404
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Test/Test1
+# DESCRIPTION
+#   for testing
+#*****
+
diff --git a/Source/Test/FileFilterSrc/test2.c b/Source/Test/FileFilterSrc/test2.c
new file mode 100644 (file)
index 0000000..53ff1a4
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Test/Test2
+# DESCRIPTION
+#   for testing
+#*****
+
diff --git a/Source/Test/FileFilterSrc/test3.c b/Source/Test/FileFilterSrc/test3.c
new file mode 100644 (file)
index 0000000..1968aca
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Test/Test3
+# DESCRIPTION
+#   for testing
+#*****
+
diff --git a/Source/Test/FileFilterSrc/test4.c b/Source/Test/FileFilterSrc/test4.c
new file mode 100644 (file)
index 0000000..c251968
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Test/Test4
+# DESCRIPTION
+#   for testing
+#*****
+
diff --git a/Source/Test/FileFilterSrc_2/AcceptDir/accept.h b/Source/Test/FileFilterSrc_2/AcceptDir/accept.h
new file mode 100644 (file)
index 0000000..a6986b2
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Accept/TT3
+# NAME
+#   TT3
+#****
+
diff --git a/Source/Test/FileFilterSrc_2/accept.c b/Source/Test/FileFilterSrc_2/accept.c
new file mode 100644 (file)
index 0000000..dcd65c4
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Accept/TT
+# NAME
+#   TT
+#****
+
diff --git a/Source/Test/FileFilterSrc_2/accept.pl b/Source/Test/FileFilterSrc_2/accept.pl
new file mode 100644 (file)
index 0000000..bd20a56
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Accept/TT2
+# NAME
+#   TT2
+#****
+
diff --git a/Source/Test/FileFilterSrc_2/ignore.ign b/Source/Test/FileFilterSrc_2/ignore.ign
new file mode 100644 (file)
index 0000000..6f3375e
--- /dev/null
@@ -0,0 +1,4 @@
+#****f* Ignore/Ignore
+# Name
+#   Ignore
+#***
diff --git a/Source/Test/FileFilterSrc_2/ignore.xxx b/Source/Test/FileFilterSrc_2/ignore.xxx
new file mode 100644 (file)
index 0000000..6f3375e
--- /dev/null
@@ -0,0 +1,4 @@
+#****f* Ignore/Ignore
+# Name
+#   Ignore
+#***
diff --git a/Source/Test/ROBOTest.pm b/Source/Test/ROBOTest.pm
new file mode 100644 (file)
index 0000000..5092237
--- /dev/null
@@ -0,0 +1,57 @@
+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;
+
diff --git a/Source/Test/RoboHeaders/rbh_test1.dat b/Source/Test/RoboHeaders/rbh_test1.dat
new file mode 100644 (file)
index 0000000..6e536e0
--- /dev/null
@@ -0,0 +1,20 @@
+
+
+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 )
+{
+
+
+}
+
diff --git a/Source/Test/Scr1/Subdir/ccfile.dat b/Source/Test/Scr1/Subdir/ccfile.dat
new file mode 100644 (file)
index 0000000..a78b3a5
--- /dev/null
@@ -0,0 +1,9 @@
+#****h* Bbb__level2__/Dbb__level3__
+# FUNCTION
+#   Test. Level 3.
+#******
+
+#****v* Dbb__level3/__var1__
+# FUNCTION
+#   Test.
+#****
diff --git a/Source/Test/Scr1/aafile.dat b/Source/Test/Scr1/aafile.dat
new file mode 100644 (file)
index 0000000..82acfa9
--- /dev/null
@@ -0,0 +1,27 @@
+
+#****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.
+#******
+
diff --git a/Source/Test/Scr1/bbfile.dat b/Source/Test/Scr1/bbfile.dat
new file mode 100644 (file)
index 0000000..4e0d208
--- /dev/null
@@ -0,0 +1,71 @@
+
+#****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
+#
+#******
+
+
diff --git a/Source/Test/Scr2/master_index_test.dat b/Source/Test/Scr2/master_index_test.dat
new file mode 100644 (file)
index 0000000..639275f
--- /dev/null
@@ -0,0 +1,23 @@
+/****if* Test/Internal1
+ * NAME
+ *   Internal1
+ *
+ *****
+ */
+
+/****f* Test/Public1
+ * NAME
+ *   Public1
+ *
+ *****
+ */
+
+/****iv* Test/Internal_Var1
+ * NAME
+ *   Internal1
+ *
+ *****
+ */
+
+
+
diff --git a/Source/Test/SortSource/sorted.c b/Source/Test/SortSource/sorted.c
new file mode 100644 (file)
index 0000000..4f97a4d
--- /dev/null
@@ -0,0 +1,21 @@
+#****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 -- 
+#****
+
+
diff --git a/Source/Test/SortSource/unsorted.c b/Source/Test/SortSource/unsorted.c
new file mode 100644 (file)
index 0000000..dec4781
--- /dev/null
@@ -0,0 +1,46 @@
+#****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 -- 
+#****
+
+
diff --git a/Source/Test/alt_rc_file.rc b/Source/Test/alt_rc_file.rc
new file mode 100644 (file)
index 0000000..402dbf3
--- /dev/null
@@ -0,0 +1,6 @@
+options:
+    --src
+    ./option_rc_test.dat
+    --doc
+    ./option_rc_test
+    --singlefile
diff --git a/Source/Test/cmode_test.dat b/Source/Test/cmode_test.dat
new file mode 100644 (file)
index 0000000..2c065fa
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 */
+}
+
+/**********/
diff --git a/Source/Test/cmode_test1.pl b/Source/Test/cmode_test1.pl
new file mode 100755 (executable)
index 0000000..69d6789
--- /dev/null
@@ -0,0 +1,33 @@
+#!/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;
+
+#*****
diff --git a/Source/Test/doc_dir_filter_test.pl b/Source/Test/doc_dir_filter_test.pl
new file mode 100755 (executable)
index 0000000..5c133d7
--- /dev/null
@@ -0,0 +1,24 @@
+#!/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;
+
+#******
diff --git a/Source/Test/doc_dir_filter_test.rc b/Source/Test/doc_dir_filter_test.rc
new file mode 100644 (file)
index 0000000..77f71ca
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/Source/Test/encoding_test.dat b/Source/Test/encoding_test.dat
new file mode 100644 (file)
index 0000000..cc8f49f
--- /dev/null
@@ -0,0 +1,6 @@
+#****h* ROBODoc/Makefile.plain
+# NAME
+#   Makefile.plain -- Plain makefile that does not need autoconf 
+# AUTHOR
+#    Mäkelä
+#*****
diff --git a/Source/Test/error_missing_header_end_test.dat b/Source/Test/error_missing_header_end_test.dat
new file mode 100644 (file)
index 0000000..d9948cd
--- /dev/null
@@ -0,0 +1,20 @@
+#****f* Test/test
+# NAME
+#    Test
+#
+#
+#
+#
+#
+
+
+
+
+#****f* Test/test2
+
+
+
+
+
+
+# there is no end marker
diff --git a/Source/Test/error_missing_header_end_test_2.dat b/Source/Test/error_missing_header_end_test_2.dat
new file mode 100644 (file)
index 0000000..3f0bc89
--- /dev/null
@@ -0,0 +1,14 @@
+#****f* Test/test
+# NAME
+#    Test
+#
+#
+#
+#
+#
+
+
+
+
+# there is no end marker
+# last line of the file...
diff --git a/Source/Test/filter_test_1.pl b/Source/Test/filter_test_1.pl
new file mode 100755 (executable)
index 0000000..316ed92
--- /dev/null
@@ -0,0 +1,17 @@
+#!/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;
+
diff --git a/Source/Test/filter_test_1.rc b/Source/Test/filter_test_1.rc
new file mode 100644 (file)
index 0000000..11debc3
--- /dev/null
@@ -0,0 +1,4 @@
+ignore files:
+    ??ADME
+    *.pas
+    ToBeIgnored
diff --git a/Source/Test/filter_test_2.pl b/Source/Test/filter_test_2.pl
new file mode 100755 (executable)
index 0000000..37d7ec9
--- /dev/null
@@ -0,0 +1,19 @@
+#!/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;
+
diff --git a/Source/Test/filter_test_2.rc b/Source/Test/filter_test_2.rc
new file mode 100644 (file)
index 0000000..1057d3d
--- /dev/null
@@ -0,0 +1,6 @@
+ignore files:
+    ToBeIgnored
+accept files:
+    *.c
+    *.pl
+    *.h
diff --git a/Source/Test/header_size_test.dat b/Source/Test/header_size_test.dat
new file mode 100644 (file)
index 0000000..0ac7ccb
--- /dev/null
@@ -0,0 +1,133 @@
+/****** 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
+ ****/
+
diff --git a/Source/Test/header_size_test.pl b/Source/Test/header_size_test.pl
new file mode 100755 (executable)
index 0000000..526f43c
--- /dev/null
@@ -0,0 +1,39 @@
+#!/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;
+
+#*****
+
diff --git a/Source/Test/header_test.dat b/Source/Test/header_test.dat
new file mode 100644 (file)
index 0000000..dd4fc85
--- /dev/null
@@ -0,0 +1,137 @@
+/****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
+.****
+
diff --git a/Source/Test/header_test.pl b/Source/Test/header_test.pl
new file mode 100755 (executable)
index 0000000..9eec9d1
--- /dev/null
@@ -0,0 +1,48 @@
+#!/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;
+
+#******
diff --git a/Source/Test/header_test2.dat b/Source/Test/header_test2.dat
new file mode 100644 (file)
index 0000000..4b921c5
--- /dev/null
@@ -0,0 +1,116 @@
+/* (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
+#****
+
diff --git a/Source/Test/header_test2.pl b/Source/Test/header_test2.pl
new file mode 100755 (executable)
index 0000000..039d4bf
--- /dev/null
@@ -0,0 +1,32 @@
+#!/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;
+
diff --git a/Source/Test/header_test3.dat b/Source/Test/header_test3.dat
new file mode 100644 (file)
index 0000000..9a5c577
--- /dev/null
@@ -0,0 +1,65 @@
+#****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__
+#
+#******
+
+
diff --git a/Source/Test/header_test3.pl b/Source/Test/header_test3.pl
new file mode 100755 (executable)
index 0000000..625ff9d
--- /dev/null
@@ -0,0 +1,30 @@
+#!/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;
+
diff --git a/Source/Test/header_test4.dat b/Source/Test/header_test4.dat
new file mode 100644 (file)
index 0000000..0d7b174
--- /dev/null
@@ -0,0 +1,22 @@
+
+#****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
+#*****
+
+
diff --git a/Source/Test/header_test4.pl b/Source/Test/header_test4.pl
new file mode 100755 (executable)
index 0000000..d3b6267
--- /dev/null
@@ -0,0 +1,47 @@
+#!/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;
+
+#******
diff --git a/Source/Test/header_test5.dat b/Source/Test/header_test5.dat
new file mode 100644 (file)
index 0000000..3a235ca
--- /dev/null
@@ -0,0 +1,86 @@
+#****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
+
+
+/*****/
+
+
diff --git a/Source/Test/header_test5.pl b/Source/Test/header_test5.pl
new file mode 100755 (executable)
index 0000000..d4c2ec1
--- /dev/null
@@ -0,0 +1,60 @@
+#!/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;
+
+#******
diff --git a/Source/Test/html_specialchar_test.dat b/Source/Test/html_specialchar_test.dat
new file mode 100644 (file)
index 0000000..1fe3cf5
--- /dev/null
@@ -0,0 +1,18 @@
+
+/****f* bad1&bad1/bad2>>bad2
+ * NAME
+ *    bad2>>bad2
+ * SEE ALSO
+ *   bad3<<bad3
+ ******
+ */
+
+
+/****f* bad1&bad1/bad3<<bad3
+ * NAME
+ *   bad3<<bad3
+ * SEE ALSO
+ *   bad2>>bad2
+ ******
+ */
+
diff --git a/Source/Test/html_specialchar_test.pl b/Source/Test/html_specialchar_test.pl
new file mode 100755 (executable)
index 0000000..4e6abe5
--- /dev/null
@@ -0,0 +1,30 @@
+#!/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;
diff --git a/Source/Test/ignore_test.dat b/Source/Test/ignore_test.dat
new file mode 100644 (file)
index 0000000..5899573
--- /dev/null
@@ -0,0 +1,14 @@
+/****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
+ *****/
+
diff --git a/Source/Test/ignore_test.pl b/Source/Test/ignore_test.pl
new file mode 100755 (executable)
index 0000000..1c732af
--- /dev/null
@@ -0,0 +1,27 @@
+#!/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;
+
diff --git a/Source/Test/ignore_test.rc b/Source/Test/ignore_test.rc
new file mode 100644 (file)
index 0000000..451c802
--- /dev/null
@@ -0,0 +1,3 @@
+ignore items:
+    FUNCTION
+    SEE ALSO
diff --git a/Source/Test/internalheader_test.dat b/Source/Test/internalheader_test.dat
new file mode 100644 (file)
index 0000000..eb3e78d
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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 --
+ *****/
+
diff --git a/Source/Test/internalheader_test.pl b/Source/Test/internalheader_test.pl
new file mode 100755 (executable)
index 0000000..a4734b1
--- /dev/null
@@ -0,0 +1,40 @@
+#!/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;
+
+#*******
+
diff --git a/Source/Test/internalheader_test.refs b/Source/Test/internalheader_test.refs
new file mode 100644 (file)
index 0000000..e88d2b0
--- /dev/null
@@ -0,0 +1 @@
+internalheader_test.xref
diff --git a/Source/Test/internalheader_test2.pl b/Source/Test/internalheader_test2.pl
new file mode 100755 (executable)
index 0000000..dc6d725
--- /dev/null
@@ -0,0 +1,31 @@
+#!/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;
diff --git a/Source/Test/item_test.dat b/Source/Test/item_test.dat
new file mode 100644 (file)
index 0000000..fe23cb6
--- /dev/null
@@ -0,0 +1,93 @@
+/****** 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 
+
+/******/
+
+
diff --git a/Source/Test/item_test.pl b/Source/Test/item_test.pl
new file mode 100755 (executable)
index 0000000..d664430
--- /dev/null
@@ -0,0 +1,28 @@
+#!/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;
diff --git a/Source/Test/latex_specialchar_test.dat b/Source/Test/latex_specialchar_test.dat
new file mode 100644 (file)
index 0000000..8971bd2
--- /dev/null
@@ -0,0 +1,38 @@
+
+/****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
+ ******
+ */
+
+
diff --git a/Source/Test/latex_specialchar_test.pl b/Source/Test/latex_specialchar_test.pl
new file mode 100755 (executable)
index 0000000..996ed54
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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;
diff --git a/Source/Test/link_test.dat b/Source/Test/link_test.dat
new file mode 100644 (file)
index 0000000..9372240
--- /dev/null
@@ -0,0 +1,83 @@
+/****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.
+ *****/
+
+
diff --git a/Source/Test/link_test.pl b/Source/Test/link_test.pl
new file mode 100755 (executable)
index 0000000..b4294a5
--- /dev/null
@@ -0,0 +1,40 @@
+#!/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");
+
+#*****
diff --git a/Source/Test/lock_test.dat b/Source/Test/lock_test.dat
new file mode 100755 (executable)
index 0000000..2835cb7
--- /dev/null
@@ -0,0 +1,32 @@
+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
+
+/******/
+
diff --git a/Source/Test/lock_test.pl b/Source/Test/lock_test.pl
new file mode 100755 (executable)
index 0000000..bb2402b
--- /dev/null
@@ -0,0 +1,23 @@
+#!/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;
+
diff --git a/Source/Test/makefile b/Source/Test/makefile
new file mode 100644 (file)
index 0000000..a6e16ef
--- /dev/null
@@ -0,0 +1,540 @@
+#****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/*.*
diff --git a/Source/Test/master_index_test.pl b/Source/Test/master_index_test.pl
new file mode 100755 (executable)
index 0000000..74da647
--- /dev/null
@@ -0,0 +1,13 @@
+#!/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;
+
diff --git a/Source/Test/multidoc_test1.pl b/Source/Test/multidoc_test1.pl
new file mode 100755 (executable)
index 0000000..0ee1daa
--- /dev/null
@@ -0,0 +1,128 @@
+#!/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;
diff --git a/Source/Test/nosort_test.pl b/Source/Test/nosort_test.pl
new file mode 100755 (executable)
index 0000000..55a92ca
--- /dev/null
@@ -0,0 +1,88 @@
+#!/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;
+
+#****
+
diff --git a/Source/Test/nosource_test.dat b/Source/Test/nosource_test.dat
new file mode 100644 (file)
index 0000000..0beaba7
--- /dev/null
@@ -0,0 +1,11 @@
+/****f* Test/test
+ * NAME
+ *   Test
+ * SOURCE
+ */
+
+should not appear;
+should not appear;
+should not appear;
+
+/****/
diff --git a/Source/Test/nosource_test.pl b/Source/Test/nosource_test.pl
new file mode 100755 (executable)
index 0000000..a227300
--- /dev/null
@@ -0,0 +1,23 @@
+#!/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;
+
diff --git a/Source/Test/option_rc_test.dat b/Source/Test/option_rc_test.dat
new file mode 100644 (file)
index 0000000..2f62720
--- /dev/null
@@ -0,0 +1,5 @@
+#****f* Test/test
+# NAME
+#   Test
+#
+#*****
diff --git a/Source/Test/option_rc_test.pl b/Source/Test/option_rc_test.pl
new file mode 100755 (executable)
index 0000000..b2db8e2
--- /dev/null
@@ -0,0 +1,14 @@
+#!/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;
diff --git a/Source/Test/outputformat_test.dat b/Source/Test/outputformat_test.dat
new file mode 100644 (file)
index 0000000..226c0f3
--- /dev/null
@@ -0,0 +1,17 @@
+/****h* ATest/Test1
+ * NAME
+ *    Test2
+ * RESULT
+ *    Test3
+ ******
+ */
+
+
+/****h* ATest/Test10
+ * NAME
+ *    Test20
+ * RESULT
+ *    Test30
+ ******
+ */
+
diff --git a/Source/Test/outputformat_test.pl b/Source/Test/outputformat_test.pl
new file mode 100755 (executable)
index 0000000..9353bba
--- /dev/null
@@ -0,0 +1,19 @@
+#!/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
+
+
diff --git a/Source/Test/parameter_test.dat b/Source/Test/parameter_test.dat
new file mode 100644 (file)
index 0000000..1918200
--- /dev/null
@@ -0,0 +1,5 @@
+
+#****f* Test/test
+# NAME
+#   test
+#***
diff --git a/Source/Test/parameter_test.pl b/Source/Test/parameter_test.pl
new file mode 100755 (executable)
index 0000000..531ef9d
--- /dev/null
@@ -0,0 +1,16 @@
+#!/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;
diff --git a/Source/Test/pipe_test.dat b/Source/Test/pipe_test.dat
new file mode 100644 (file)
index 0000000..d28b9ee
--- /dev/null
@@ -0,0 +1,31 @@
+/****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...
+ ******
+ */
diff --git a/Source/Test/pipe_test.pl b/Source/Test/pipe_test.pl
new file mode 100755 (executable)
index 0000000..5842d88
--- /dev/null
@@ -0,0 +1,51 @@
+#!/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&lt\;B&gt\;Moi!&lt\;\/B&gt\;/) {
+                       $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;
diff --git a/Source/Test/readme.txt b/Source/Test/readme.txt
new file mode 100644 (file)
index 0000000..415e926
--- /dev/null
@@ -0,0 +1,3 @@
+
+See documentation in the makefile.
+
diff --git a/Source/Test/remarkmarker_test.dat b/Source/Test/remarkmarker_test.dat
new file mode 100644 (file)
index 0000000..867d17e
--- /dev/null
@@ -0,0 +1,25 @@
+/****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
+/******/
+
+
diff --git a/Source/Test/remarkmarker_test.pl b/Source/Test/remarkmarker_test.pl
new file mode 100755 (executable)
index 0000000..ff0ea3d
--- /dev/null
@@ -0,0 +1,29 @@
+#!/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;
diff --git a/Source/Test/robodoc.rc b/Source/Test/robodoc.rc
new file mode 100644 (file)
index 0000000..e2b7460
--- /dev/null
@@ -0,0 +1,5 @@
+#options:
+#     --tell
+#
+ignore files:
+    CVS
diff --git a/Source/Test/robohdrs_test.pl b/Source/Test/robohdrs_test.pl
new file mode 100644 (file)
index 0000000..8f0ef6e
--- /dev/null
@@ -0,0 +1,13 @@
+#!/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;
+
diff --git a/Source/Test/sort_test.pl b/Source/Test/sort_test.pl
new file mode 100755 (executable)
index 0000000..6f21c45
--- /dev/null
@@ -0,0 +1,87 @@
+#!/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;
+
+#*****
diff --git a/Source/Test/tabtest.dat b/Source/Test/tabtest.dat
new file mode 100644 (file)
index 0000000..7b3b9d6
--- /dev/null
@@ -0,0 +1,15 @@
+/****h* URLTest/URLTest
+ * NAME
+ *   URLTest
+ * NOTES
+ *      dummy
+ *             XXX   1
+ *     XXX   2
+ *             XXX   3
+ *             XXX   4
+ *             XXX   5
+ *
+ *
+ *****
+ */
+
diff --git a/Source/Test/tabtest.pl b/Source/Test/tabtest.pl
new file mode 100755 (executable)
index 0000000..43b2828
--- /dev/null
@@ -0,0 +1,21 @@
+#!/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;
+
diff --git a/Source/Test/url_test.dat b/Source/Test/url_test.dat
new file mode 100644 (file)
index 0000000..0078ddb
--- /dev/null
@@ -0,0 +1,13 @@
+/****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
+ *****
+ */
+
diff --git a/Source/Test/url_test.pl b/Source/Test/url_test.pl
new file mode 100755 (executable)
index 0000000..7796eb6
--- /dev/null
@@ -0,0 +1,24 @@
+#!/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;
+
diff --git a/Source/Test/user_css.css b/Source/Test/user_css.css
new file mode 100644 (file)
index 0000000..e2c0709
--- /dev/null
@@ -0,0 +1,27 @@
+#  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
diff --git a/Source/Test/user_specified_css_file_test.pl b/Source/Test/user_specified_css_file_test.pl
new file mode 100755 (executable)
index 0000000..3d84b71
--- /dev/null
@@ -0,0 +1,28 @@
+#!/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;
+
diff --git a/Source/analyser.c b/Source/analyser.c
new file mode 100644 (file)
index 0000000..7d82641
--- /dev/null
@@ -0,0 +1,2077 @@
+/*
+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  *****/
diff --git a/Source/analyser.h b/Source/analyser.h
new file mode 100644 (file)
index 0000000..e28faf7
--- /dev/null
@@ -0,0 +1,32 @@
+#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 */
diff --git a/Source/ascii_generator.c b/Source/ascii_generator.c
new file mode 100644 (file)
index 0000000..0e35803
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+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 */
+}
diff --git a/Source/ascii_generator.h b/Source/ascii_generator.h
new file mode 100644 (file)
index 0000000..56ecbf4
--- /dev/null
@@ -0,0 +1,78 @@
+#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 */
diff --git a/Source/config.h.in b/Source/config.h.in
new file mode 100644 (file)
index 0000000..e4ab7d9
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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
diff --git a/Source/css_to_c.pl b/Source/css_to_c.pl
new file mode 100644 (file)
index 0000000..250ae48
--- /dev/null
@@ -0,0 +1,94 @@
+#!/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();
+
diff --git a/Source/directory.c b/Source/directory.c
new file mode 100644 (file)
index 0000000..000bbe5
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+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 );
+}
diff --git a/Source/directory.h b/Source/directory.h
new file mode 100644 (file)
index 0000000..275c8c1
--- /dev/null
@@ -0,0 +1,135 @@
+#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 */
diff --git a/Source/document.c b/Source/document.c
new file mode 100644 (file)
index 0000000..c1fe4b4
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+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;
+}
+
+/*******/
diff --git a/Source/document.h b/Source/document.h
new file mode 100644 (file)
index 0000000..3a2634c
--- /dev/null
@@ -0,0 +1,128 @@
+#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 */
diff --git a/Source/file.c b/Source/file.c
new file mode 100644 (file)
index 0000000..5cc8b25
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+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;
+}
diff --git a/Source/file.h b/Source/file.h
new file mode 100644 (file)
index 0000000..ac7d13b
--- /dev/null
@@ -0,0 +1,87 @@
+#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 */
diff --git a/Source/generator.c b/Source/generator.c
new file mode 100644 (file)
index 0000000..491aa22
--- /dev/null
@@ -0,0 +1,2845 @@
+// 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;
+    }
+}
+
+/*****/
diff --git a/Source/generator.h b/Source/generator.h
new file mode 100644 (file)
index 0000000..204825d
--- /dev/null
@@ -0,0 +1,140 @@
+#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 */
diff --git a/Source/globals.c b/Source/globals.c
new file mode 100644 (file)
index 0000000..9edb766
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+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 );
+    }
+}
+
+/******/
diff --git a/Source/globals.h b/Source/globals.h
new file mode 100644 (file)
index 0000000..c86e17b
--- /dev/null
@@ -0,0 +1,90 @@
+#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 */
diff --git a/Source/headers.c b/Source/headers.c
new file mode 100644 (file)
index 0000000..bd2daed
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+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;
+}
diff --git a/Source/headers.h b/Source/headers.h
new file mode 100644 (file)
index 0000000..ad3acab
--- /dev/null
@@ -0,0 +1,215 @@
+#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 */
diff --git a/Source/headertypes.c b/Source/headertypes.c
new file mode 100644 (file)
index 0000000..ece8a7a
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+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
diff --git a/Source/headertypes.h b/Source/headertypes.h
new file mode 100644 (file)
index 0000000..f3111b4
--- /dev/null
@@ -0,0 +1,71 @@
+#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 */
diff --git a/Source/html_generator.c b/Source/html_generator.c
new file mode 100644 (file)
index 0000000..145d5e6
--- /dev/null
@@ -0,0 +1,2384 @@
+/*
+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, "&lt;" );
+        break;
+    case '>':
+        fprintf( dest_doc, "&gt;" );
+        break;
+    case '&':
+        fprintf( dest_doc, "&amp;" );
+        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" );
+}
diff --git a/Source/html_generator.h b/Source/html_generator.h
new file mode 100644 (file)
index 0000000..f9b752e
--- /dev/null
@@ -0,0 +1,167 @@
+#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 */
diff --git a/Source/items.c b/Source/items.c
new file mode 100644 (file)
index 0000000..f9598f2
--- /dev/null
@@ -0,0 +1,340 @@
+// 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;
+}
+
+/******/
diff --git a/Source/items.h b/Source/items.h
new file mode 100644 (file)
index 0000000..85606f8
--- /dev/null
@@ -0,0 +1,130 @@
+#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 */
diff --git a/Source/latex_generator.c b/Source/latex_generator.c
new file mode 100644 (file)
index 0000000..fb60b5b
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+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 );
+}
+
+/******/
diff --git a/Source/latex_generator.h b/Source/latex_generator.h
new file mode 100644 (file)
index 0000000..804a3fb
--- /dev/null
@@ -0,0 +1,125 @@
+#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 */
diff --git a/Source/links.c b/Source/links.c
new file mode 100644 (file)
index 0000000..db8b7e3
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+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 );
+    }
+}
+
+/******/
+
diff --git a/Source/links.h b/Source/links.h
new file mode 100644 (file)
index 0000000..ca50cbb
--- /dev/null
@@ -0,0 +1,76 @@
+#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 */
diff --git a/Source/lua_generator.c b/Source/lua_generator.c
new file mode 100644 (file)
index 0000000..29f1d8f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+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
diff --git a/Source/lua_generator.h b/Source/lua_generator.h
new file mode 100644 (file)
index 0000000..8161187
--- /dev/null
@@ -0,0 +1,28 @@
+#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 */
diff --git a/Source/makefile.am b/Source/makefile.am
new file mode 100644 (file)
index 0000000..1f16f57
--- /dev/null
@@ -0,0 +1,38 @@
+# 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 
diff --git a/Source/makefile.bcc b/Source/makefile.bcc
new file mode 100644 (file)
index 0000000..0ccbdaa
--- /dev/null
@@ -0,0 +1,182 @@
+#****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 
+
diff --git a/Source/makefile.mingw b/Source/makefile.mingw
new file mode 100644 (file)
index 0000000..f0e9907
--- /dev/null
@@ -0,0 +1,255 @@
+#****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
diff --git a/Source/makefile.mingw-cygwin b/Source/makefile.mingw-cygwin
new file mode 100644 (file)
index 0000000..91797f9
--- /dev/null
@@ -0,0 +1,279 @@
+# 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
diff --git a/Source/makefile.plain b/Source/makefile.plain
new file mode 100644 (file)
index 0000000..0e370fb
--- /dev/null
@@ -0,0 +1,262 @@
+# -*-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
+
diff --git a/Source/makefile.win32 b/Source/makefile.win32
new file mode 100644 (file)
index 0000000..9d1efd7
--- /dev/null
@@ -0,0 +1,105 @@
+#****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
+#******
diff --git a/Source/makefile.wingcc b/Source/makefile.wingcc
new file mode 100644 (file)
index 0000000..a17945c
--- /dev/null
@@ -0,0 +1 @@
+# Obsolete
diff --git a/Source/optioncheck.c b/Source/optioncheck.c
new file mode 100644 (file)
index 0000000..6067211
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+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;
+}
+
+/*****/
diff --git a/Source/optioncheck.h b/Source/optioncheck.h
new file mode 100644 (file)
index 0000000..286822c
--- /dev/null
@@ -0,0 +1,31 @@
+#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 */
diff --git a/Source/part.c b/Source/part.c
new file mode 100644 (file)
index 0000000..1ec4fea
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+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;
+    }
+}
diff --git a/Source/part.h b/Source/part.h
new file mode 100644 (file)
index 0000000..4f7b753
--- /dev/null
@@ -0,0 +1,85 @@
+#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 */
diff --git a/Source/path.c b/Source/path.c
new file mode 100644 (file)
index 0000000..38e81d0
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+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 );
+}
+
diff --git a/Source/path.h b/Source/path.h
new file mode 100644 (file)
index 0000000..a5eef87
--- /dev/null
@@ -0,0 +1,56 @@
+#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 */
diff --git a/Source/roboconfig.c b/Source/roboconfig.c
new file mode 100644 (file)
index 0000000..e8b8532
--- /dev/null
@@ -0,0 +1,1401 @@
+// 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 );
+}
diff --git a/Source/roboconfig.h b/Source/roboconfig.h
new file mode 100644 (file)
index 0000000..0d7183a
--- /dev/null
@@ -0,0 +1,195 @@
+#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
diff --git a/Source/robodoc.c b/Source/robodoc.c
new file mode 100644 (file)
index 0000000..2cf5332
--- /dev/null
@@ -0,0 +1,1473 @@
+/*
+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
+}
diff --git a/Source/robodoc.h b/Source/robodoc.h
new file mode 100644 (file)
index 0000000..086c904
--- /dev/null
@@ -0,0 +1,141 @@
+#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 */
+
diff --git a/Source/robodoc_basic_style.css b/Source/robodoc_basic_style.css
new file mode 100644 (file)
index 0000000..55d8b30
--- /dev/null
@@ -0,0 +1,298 @@
+/****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;}
+}
diff --git a/Source/robohdrs.c b/Source/robohdrs.c
new file mode 100644 (file)
index 0000000..a69fbfa
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+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  */
+
diff --git a/Source/rtf_generator.c b/Source/rtf_generator.c
new file mode 100644 (file)
index 0000000..ff4ef12
--- /dev/null
@@ -0,0 +1,327 @@
+// 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 );
+    }
+}
diff --git a/Source/rtf_generator.h b/Source/rtf_generator.h
new file mode 100644 (file)
index 0000000..b0a1bce
--- /dev/null
@@ -0,0 +1,89 @@
+#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 */
diff --git a/Source/t/ROBOTestFrame.pm b/Source/t/ROBOTestFrame.pm
new file mode 100644 (file)
index 0000000..b06f6a7
--- /dev/null
@@ -0,0 +1,298 @@
+# 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;
diff --git a/Source/t/Robodoc.pm b/Source/t/Robodoc.pm
new file mode 100644 (file)
index 0000000..625027c
--- /dev/null
@@ -0,0 +1,3 @@
+0;
+# OBSOLETE
+#
diff --git a/Source/t/TestData/iso8859-1_german_header_c.xxd b/Source/t/TestData/iso8859-1_german_header_c.xxd
new file mode 100644 (file)
index 0000000..1030f9c
--- /dev/null
@@ -0,0 +1,35 @@
+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                        ***....
diff --git a/Source/t/TestData/iso8859-1_german_robodoc_rc.xxd b/Source/t/TestData/iso8859-1_german_robodoc_rc.xxd
new file mode 100644 (file)
index 0000000..f790322
--- /dev/null
@@ -0,0 +1,5 @@
+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                                  ...
diff --git a/Source/t/TestData/robodoc_unix_rc.xxd b/Source/t/TestData/robodoc_unix_rc.xxd
new file mode 100644 (file)
index 0000000..8484991
--- /dev/null
@@ -0,0 +1,4 @@
+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.
diff --git a/Source/t/TestData/robodoc_windows_rc.xxd b/Source/t/TestData/robodoc_windows_rc.xxd
new file mode 100644 (file)
index 0000000..23a1246
--- /dev/null
@@ -0,0 +1,4 @@
+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..
diff --git a/Source/t/ascii.t b/Source/t/ascii.t
new file mode 100644 (file)
index 0000000..a2c74d2
--- /dev/null
@@ -0,0 +1,118 @@
+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();
+            }
+        }
+    }
+}
+#****
+
diff --git a/Source/t/basics.t b/Source/t/basics.t
new file mode 100644 (file)
index 0000000..237cfe9
--- /dev/null
@@ -0,0 +1,193 @@
+#!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;
+
diff --git a/Source/t/encoding.t b/Source/t/encoding.t
new file mode 100644 (file)
index 0000000..1d803e6
--- /dev/null
@@ -0,0 +1,52 @@
+#!/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();
+}
+
+
diff --git a/Source/t/errors.t b/Source/t/errors.t
new file mode 100644 (file)
index 0000000..80caf34
--- /dev/null
@@ -0,0 +1,224 @@
+#!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();
+}
diff --git a/Source/t/headers.t b/Source/t/headers.t
new file mode 100644 (file)
index 0000000..958877f
--- /dev/null
@@ -0,0 +1,278 @@
+#!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();
+}
+
+#*****
diff --git a/Source/t/items.t b/Source/t/items.t
new file mode 100644 (file)
index 0000000..6dd3e8d
--- /dev/null
@@ -0,0 +1,107 @@
+#!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();
+}
+#*******
+
+
diff --git a/Source/t/latex.t b/Source/t/latex.t
new file mode 100644 (file)
index 0000000..71c04c3
--- /dev/null
@@ -0,0 +1,117 @@
+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();
+            }
+        }
+    }
+}
+#****
+
diff --git a/Source/t/lib/IPC/Run.pm b/Source/t/lib/IPC/Run.pm
new file mode 100644 (file)
index 0000000..61ca9c2
--- /dev/null
@@ -0,0 +1,4476 @@
+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 ;
diff --git a/Source/t/lib/IPC/Run/Debug.pm b/Source/t/lib/IPC/Run/Debug.pm
new file mode 100644 (file)
index 0000000..9b2c452
--- /dev/null
@@ -0,0 +1,311 @@
+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 ;
diff --git a/Source/t/lib/IPC/Run/IO.pm b/Source/t/lib/IPC/Run/IO.pm
new file mode 100644 (file)
index 0000000..94c8c88
--- /dev/null
@@ -0,0 +1,562 @@
+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 ;
diff --git a/Source/t/lib/IPC/Run/Timer.pm b/Source/t/lib/IPC/Run/Timer.pm
new file mode 100644 (file)
index 0000000..614f84e
--- /dev/null
@@ -0,0 +1,688 @@
+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 ;
diff --git a/Source/t/lib/IPC/Run/Win32Helper.pm b/Source/t/lib/IPC/Run/Win32Helper.pm
new file mode 100644 (file)
index 0000000..de26abb
--- /dev/null
@@ -0,0 +1,481 @@
+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 ;
diff --git a/Source/t/lib/IPC/Run/Win32IO.pm b/Source/t/lib/IPC/Run/Win32IO.pm
new file mode 100644 (file)
index 0000000..ed79161
--- /dev/null
@@ -0,0 +1,556 @@
+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;
diff --git a/Source/t/lib/IPC/Run/Win32Pump.pm b/Source/t/lib/IPC/Run/Win32Pump.pm
new file mode 100644 (file)
index 0000000..6a2d221
--- /dev/null
@@ -0,0 +1,162 @@
+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 ;
diff --git a/Source/t/lib/Test/File.pm b/Source/t/lib/Test/File.pm
new file mode 100644 (file)
index 0000000..d0e4a0a
--- /dev/null
@@ -0,0 +1,1090 @@
+# $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";
diff --git a/Source/t/makefile b/Source/t/makefile
new file mode 100644 (file)
index 0000000..17c1fdd
--- /dev/null
@@ -0,0 +1,3 @@
+test:
+       prove -l
+
diff --git a/Source/t/one_file_per_header.t b/Source/t/one_file_per_header.t
new file mode 100644 (file)
index 0000000..daac3b0
--- /dev/null
@@ -0,0 +1,57 @@
+#!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();
+}
+
+#*******
+
diff --git a/Source/t/pipes.t b/Source/t/pipes.t
new file mode 100644 (file)
index 0000000..7fe13ed
--- /dev/null
@@ -0,0 +1,83 @@
+#/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();
+}
+#****
+
+
+
diff --git a/Source/t/readme.txt b/Source/t/readme.txt
new file mode 100644 (file)
index 0000000..77645fc
--- /dev/null
@@ -0,0 +1,28 @@
+#****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.
+#
+#*****
+#
+
diff --git a/Source/t/robodoc_rc.t b/Source/t/robodoc_rc.t
new file mode 100644 (file)
index 0000000..d771dcf
--- /dev/null
@@ -0,0 +1,117 @@
+#!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();
+}
+
+#******
+
diff --git a/Source/t/wiki_formatting.t b/Source/t/wiki_formatting.t
new file mode 100644 (file)
index 0000000..abf31e1
--- /dev/null
@@ -0,0 +1,78 @@
+#!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();
+}
+
+#******
+
+
diff --git a/Source/test_generator.c b/Source/test_generator.c
new file mode 100644 (file)
index 0000000..16d619e
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+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" );
+}
diff --git a/Source/test_generator.h b/Source/test_generator.h
new file mode 100644 (file)
index 0000000..7ca1646
--- /dev/null
@@ -0,0 +1,118 @@
+#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 */
diff --git a/Source/testheaders.c b/Source/testheaders.c
new file mode 100644 (file)
index 0000000..9fd234c
--- /dev/null
@@ -0,0 +1 @@
+/* obsolete */
diff --git a/Source/todo.pl b/Source/todo.pl
new file mode 100644 (file)
index 0000000..58ce2fd
--- /dev/null
@@ -0,0 +1,60 @@
+#!/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;
+
+
diff --git a/Source/troff_generator.c b/Source/troff_generator.c
new file mode 100644 (file)
index 0000000..c44f926
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+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
+
diff --git a/Source/troff_generator.h b/Source/troff_generator.h
new file mode 100644 (file)
index 0000000..366962f
--- /dev/null
@@ -0,0 +1,80 @@
+#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 */
diff --git a/Source/unittest.c b/Source/unittest.c
new file mode 100644 (file)
index 0000000..eba1229
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+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;
+}
diff --git a/Source/unittest.h b/Source/unittest.h
new file mode 100644 (file)
index 0000000..5c26db2
--- /dev/null
@@ -0,0 +1,28 @@
+#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 */
diff --git a/Source/util.c b/Source/util.c
new file mode 100644 (file)
index 0000000..c32fa02
--- /dev/null
@@ -0,0 +1,1495 @@
+// 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 ) );
+}
diff --git a/Source/util.h b/Source/util.h
new file mode 100644 (file)
index 0000000..82799de
--- /dev/null
@@ -0,0 +1,184 @@
+#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 */
diff --git a/Source/xmldocbook_generator.c b/Source/xmldocbook_generator.c
new file mode 100644 (file)
index 0000000..9488b5b
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+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, "&lt;" );
+        break;
+    case '>':
+        fprintf( dest_doc, "&gt;" );
+        break;
+    case '&':
+        fprintf( dest_doc, "&amp;" );
+        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>" );
+}
diff --git a/Source/xmldocbook_generator.h b/Source/xmldocbook_generator.h
new file mode 100644 (file)
index 0000000..65e8dde
--- /dev/null
@@ -0,0 +1,117 @@
+#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 */
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..c37f97a
--- /dev/null
+++ b/TODO
@@ -0,0 +1,34 @@
+
+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
+
diff --git a/UPGRADE b/UPGRADE
new file mode 100644 (file)
index 0000000..8b9921c
--- /dev/null
+++ b/UPGRADE
@@ -0,0 +1,73 @@
+$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 
+
+
+
diff --git a/Win32/robodoc.dsp b/Win32/robodoc.dsp
new file mode 100644 (file)
index 0000000..db50b91
--- /dev/null
@@ -0,0 +1,584 @@
+# 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
+
diff --git a/Win32/robodoc.dsw b/Win32/robodoc.dsw
new file mode 100644 (file)
index 0000000..9429ea0
--- /dev/null
@@ -0,0 +1,58 @@
+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>
+
+{{{
+
+}}}
+
+
+
+###############################################################################
+
+
+
diff --git a/Win32/robodoc.sln b/Win32/robodoc.sln
new file mode 100644 (file)
index 0000000..e221441
--- /dev/null
@@ -0,0 +1,23 @@
+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
diff --git a/Win32/robodoc.vcproj b/Win32/robodoc.vcproj
new file mode 100644 (file)
index 0000000..bd17ea4
--- /dev/null
@@ -0,0 +1,619 @@
+<?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>
diff --git a/bump.pl b/bump.pl
new file mode 100755 (executable)
index 0000000..89b9082
--- /dev/null
+++ b/bump.pl
@@ -0,0 +1,200 @@
+#!/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/>.
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..ff1db5c
--- /dev/null
@@ -0,0 +1,61 @@
+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
diff --git a/do.sh b/do.sh
new file mode 100755 (executable)
index 0000000..45e582f
--- /dev/null
+++ b/do.sh
@@ -0,0 +1,57 @@
+#!/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
diff --git a/dos2unix.pl b/dos2unix.pl
new file mode 100644 (file)
index 0000000..c5f6a25
--- /dev/null
@@ -0,0 +1,84 @@
+#!/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;
+
diff --git a/makefile.am b/makefile.am
new file mode 100644 (file)
index 0000000..0333bf2
--- /dev/null
@@ -0,0 +1,83 @@
+# 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
+#
diff --git a/osx/License.rtf.in b/osx/License.rtf.in
new file mode 100644 (file)
index 0000000..44db3f7
--- /dev/null
@@ -0,0 +1,15 @@
+{\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\
+\
+}
diff --git a/osx/ReadMe.rtf.in b/osx/ReadMe.rtf.in
new file mode 100644 (file)
index 0000000..98f54ba
--- /dev/null
@@ -0,0 +1,13 @@
+{\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
diff --git a/osx/Welcome.rtf b/osx/Welcome.rtf
new file mode 100644 (file)
index 0000000..95b1c42
--- /dev/null
@@ -0,0 +1,7 @@
+{\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.}
diff --git a/osx/dmg.sh b/osx/dmg.sh
new file mode 100755 (executable)
index 0000000..11fe295
--- /dev/null
@@ -0,0 +1,103 @@
+#!/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
diff --git a/robodoc.spec.in b/robodoc.spec.in
new file mode 100644 (file)
index 0000000..15c8cd9
--- /dev/null
@@ -0,0 +1,66 @@
+# $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
diff --git a/rpm.mk b/rpm.mk
new file mode 100644 (file)
index 0000000..ca14afd
--- /dev/null
+++ b/rpm.mk
@@ -0,0 +1,200 @@
+#****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)
+
+#****
diff --git a/todo.pl b/todo.pl
new file mode 100644 (file)
index 0000000..e4dba64
--- /dev/null
+++ b/todo.pl
@@ -0,0 +1,100 @@
+#!/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;
+
+