changeset 2:6c41a0b43e58 release_0_3

Tema 0.3 (imported from CVS).
author Mikhail Kryshen <mikhail@kryshen.net>
date Tue, 19 Feb 2008 20:32:17 +0300
parents 548a93c24e55
children a20217d78068
files .cvsignore COPYING COPYING.LESSER COPYING.apache NOTICE TODO build.xml dist/.cvsignore dist/biotopes/biotope-top.sql dist/biotopes/biotope.sql dist/biotopes/biotope.template dist/biotopes/brief.dtd dist/biotopes/class.sql dist/biotopes/classes.template dist/biotopes/doc/article.txt dist/biotopes/doc/readme.html dist/biotopes/doc/readme.txt dist/biotopes/main.template dist/biotopes/photo.sql dist/biotopes/photo.template dist/biotopes/plant.sql dist/biotopes/plants.template dist/biotopes/tema.properties dist/lib/commons-cli-1.1.jar dist/tema.properties doc/.cvsignore doc/manual/.cvsignore doc/manual/document.tema doc/manual/index.html doc/manual/manual.tema misc/biotopes/biotope-top.sql misc/biotopes/biotope.sql misc/biotopes/biotope.template misc/biotopes/brief.dtd misc/biotopes/class.sql misc/biotopes/classes.template misc/biotopes/doc/article.txt misc/biotopes/doc/readme.html misc/biotopes/doc/readme.txt misc/biotopes/main.template misc/biotopes/photo.sql misc/biotopes/photo.template misc/biotopes/plant.sql misc/biotopes/plants.template misc/biotopes/tema.properties misc/manual-ru-old/index.html misc/util/header misc/util/header.gpl misc/util/update_headers.pl nbproject/.cvsignore nbproject/project.xml res/kryshen/tema/demo/demo.template src/Manifest.mf src/kryshen/tema/Context.java src/kryshen/tema/Function.java src/kryshen/tema/FunctionDataParser.java src/kryshen/tema/Functions.java src/kryshen/tema/GlobalContext.java src/kryshen/tema/Tema.java src/kryshen/tema/TemplateException.java src/kryshen/tema/TemplateParser.java src/kryshen/tema/TemplateReader.java src/kryshen/tema/ant/TemaTask.java src/kryshen/tema/demo/DemoFrame.java src/kryshen/tema/demo/Hello.java src/kryshen/tema/functions/Control.java src/kryshen/tema/functions/Database.java src/kryshen/tema/functions/Define.java src/kryshen/tema/functions/IO.java src/kryshen/tema/functions/ImageConverter.java src/kryshen/tema/functions/Logics.java src/kryshen/tema/functions/ReplaceWriter.java src/kryshen/tema/functions/Standard.java src/kryshen/tema/functions/Strings.java src/kryshen/tema/io/CopyWriter.java src/kryshen/tema/io/NullWriter.java src/kryshen/tema/io/ReplaceWriter.java src/kryshen/tema/io/TemplateReader.java test/demo test/demo.bat test/include.template test/main.template test/tema.properties
diffstat 83 files changed, 5518 insertions(+), 2668 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.cvsignore	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,1 @@
+build
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING	Tue Feb 19 20:32:17 2008 +0300
@@ -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>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING.LESSER	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,165 @@
+		   GNU LESSER 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.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING.apache	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NOTICE	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,13 @@
+Tema
+Copyright 2006-2008 Mikhail Kryshen <mikhail@kryshen.pp.ru>
+
+This is free software. You may redistribute copies of it under the terms of
+the GNU Lesser General Public License version 3 or later.
+
+
+
+Apache Commons CLI
+Copyright 2001-2007 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
--- a/TODO	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-if, <, >, =, +, -, *, /
--- a/build.xml	Thu Dec 14 23:22:05 2006 +0300
+++ b/build.xml	Tue Feb 19 20:32:17 2008 +0300
@@ -1,44 +1,113 @@
 <?xml version="1.0"?>
 <project name="tema" default="dist" basedir=".">
-
+    
+    <property name="version"  value="0.3"/>
+    
     <property name="src"      value="src"/>
     <property name="build"    value="build"/>
     <property name="dist"     value="dist"/>
     <property name="res"      value="res"/>
     <property name="doc"      value="doc"/>
+    <property name="lib"      value="${dist}/lib"/>
     <property name="jar_file" value="tema.jar"/>
-
+    
+    <property name="ant_jar" value="/usr/share/java/ant.jar"/>
+    
+    <path id="project.libs">
+        <fileset dir="${lib}">
+            <include name="**/*.jar"/>
+        </fileset>
+    </path>
+    
     <target name="init">
-	<tstamp/>
-	<mkdir dir="${build}"/>
+        <tstamp/>
+        <mkdir dir="${build}"/>
     </target>
-
+    
     <target name="compile" depends="init">
         <javac srcdir="${src}" destdir="${build}"
                deprecation="on" optimize="on" debug="on">
-          <!-- <compilerarg value="-Xlint:unchecked"/> -->
+            <compilerarg value="-Xlint:unchecked"/>
+            <classpath refid="project.libs"/>
         </javac>
+        
+        <copy todir="${build}">
+            <fileset dir="${res}"/>
+        </copy>
     </target>
-
+    
     <target name="dist" depends="compile">
-	<jar jarfile="${dist}/${jar_file}" manifest="${src}/Manifest.mf">
-	    <fileset dir="${build}" includes="**/*.class"/>
-	    <fileset dir="${res}" includes="**/*"/>
-	</jar>
+        <jar jarfile="${dist}/${jar_file}" manifest="${src}/Manifest.mf">
+            <fileset dir="${build}" includes="**/*.class"/>
+            <fileset dir="${build}" includes="**/*.template"/>
+        </jar>
+    </target>
+      
+    <target name="run" depends="compile">
+        <java classname="kryshen.tema.Tema" fork="true">
+            <classpath>
+                <pathelement location="${build}"/>
+                <path refid="project.libs"/>
+            </classpath>
+        </java>
+    </target>
+    
+    <target name="run.demo" depends="compile">
+        <java classname="kryshen.tema.Tema" fork="true">
+            <classpath>
+                <pathelement location="${build}"/>
+                <path refid="project.libs"/>
+            </classpath>
+            <arg value="-demo"/>
+        </java>        
+    </target>
+    
+    <target name="doc.manual" depends="dist">
+        <taskdef name="tema"
+                 classname="kryshen.tema.ant.TemaTask"
+                 classpath="${dist}/tema.jar"/>
+                 
+        <tema infile="${doc}/manual/manual.tema"
+              outfile="${doc}/manual/manual.html"
+              basedir="${doc}/manual"/>
     </target>
 
-    <target name="javadoc" depends="init">
+    <target name="doc.api" depends="init">
         <javadoc destdir="${doc}/api" sourcepath="${src}"
                  packagenames="kryshen.tema.*">
+            <classpath> 
+                <path refid="project.libs"/>
+                <pathelement location="${ant_jar}"/>
+            </classpath>
         </javadoc>
     </target>
- 
-    <target name="clean">
-	<delete>
-	    <fileset dir="${build}" includes="**/*.class"/>
-	</delete>
-	<delete file="${dist}/${jar_file}"/>
-	<delete dir="${doc}/api"/>
+    
+    <target name="package.tar" depends="dist,doc.manual,doc.api">
+        <tar destfile="${dist}/${ant.project.name}-${version}.tar.gz"
+             compression="gzip">
+            <tarfileset dir="${basedir}"
+                        prefix="${ant.project.name}-${version}">
+                <exclude name="${build}/**"/>
+                <exclude name="misc/**"/>
+                <exclude name="test/**"/>
+                <exclude name="nbproject/**"/>
+                <exclude name="${dist}/${ant.project.name}-*.tar.gz"/>
+                <exclude name="${dist}/tema"/>
+            </tarfileset>
+            <tarfileset dir="${basedir}"
+                        prefix="${ant.project.name}-${version}"
+                        mode="755">
+                <include name="${dist}/tema"/>
+            </tarfileset>
+        </tar>
+    </target>
+    
+    <target name="clean" depends="init">
+        <delete>
+            <fileset dir="${build}" includes="**/*.class"/>
+        </delete>
+        <delete file="${dist}/${jar_file}"/>
+        <delete dir="${doc}/api"/>
     </target>
     
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dist/.cvsignore	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,2 @@
+tema.jar
+tema-*.tar.gz
--- a/dist/biotopes/biotope-top.sql	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1) AND
-Right(t2.rangcode, 8) = '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3) AND
-Right(t2.rangcode, 6) = '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5) AND
-Right(t2.rangcode, 4) = '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7) AND
-Right(t2.rangcode, 2) = '00') AS class3
-
-FROM biotopelist AS t1
-WHERE Right(t1.rangcode, 2) <> '00' AND rusname
--- a/dist/biotopes/biotope.sql	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-AND Right(t2.rangcode, 8)  = '00000000'
-AND Right(t1.rangcode, 8) <> '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
-AND Right(t2.rangcode, 6)  = '000000'
-AND Right(t1.rangcode, 6) <> '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
-AND Right(t2.rangcode, 4)  = '0000'
-AND Right(t1.rangcode, 4) <> '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
-AND Right(t2.rangcode, 2)  = '00'
-AND Right(t1.rangcode, 2) <> '00') AS class3
-
-FROM biotopelist AS t1
-WHERE rusname
--- a/dist/biotopes/biotope.template	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: <%xml_escape db\rangcode%> -->
-<_ show="0">
-  < display="0">
-    <%optional:<_ ="1" file="<%xml_escape image:maps/<%db\rangcode%>.gif maps/<%db\code%>.png png%>"/>%>
-<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
-  </>
-  <><%xml_escape db\rusname%></>
-  <><%xml_escape db\class0%></>
-  <1><%xml_escape db\class1%></1>
-  <2><%xml_escape db\class2%></2>
-  <3><%xml_escape db\class3%></3>
-  <_ rows="3"><%xml_cdata db\descript%></_>
-  <_ rows="3"><%xml_cdata db\geobotdescr%></_>
-  <_><%xml_escape db\contributors%></_>
-</_>
--- a/dist/biotopes/brief.dtd	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-<?xml encoding="ISO-8859-5"?>
-<!ELEMENT _ (?, , , 1, 2, 3, _, _, _)>
-<!ELEMENT  (|_)*>
-<!ELEMENT  EMPTY>
-<!ELEMENT _ EMPTY>
-<!ELEMENT  (#PCDATA)>
-<!ELEMENT  (#PCDATA)>
-<!ELEMENT 1 (#PCDATA)>
-<!ELEMENT 2 (#PCDATA)>
-<!ELEMENT 3 (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ATTLIST _ show (0|1) "1"> 
-<!ATTLIST 
-	display CDATA #FIXED "0">
-<!ATTLIST 
-	num ID #REQUIRED
-	file CDATA #REQUIRED
-	big CDATA #REQUIRED
-	text CDATA #IMPLIED
-	 CDATA #IMPLIED>
-<!ATTLIST _
-	num ID #REQUIRED
-	file CDATA #REQUIRED>
-<!ATTLIST _ rows CDATA #FIXED "3">
-<!ATTLIST _ rows CDATA #FIXED "3">
--- a/dist/biotopes/class.sql	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
-  AND EXISTS
-    (SELECT NULL FROM biotopelist AS t2
-     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-     AND Right(t2.rangcode, 8) <> '00000000')
--- a/dist/biotopes/classes.template	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:class_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  < id="<%get\NUMBER%>"><%xml_escape db\rusname%></>%]%>
-</>
--- a/dist/biotopes/doc/article.txt	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,391 +0,0 @@
-*     XML-  .
-
-      " 
-"   Microsoft Access  XML- 
-       DbReader.
-
- DbReader     
-      .  
-        
-    .   
- SQL-,     
-.
-
-
-*  :
-
-    <%<_>{:|\}<>%>
-   
-
-<_> -   ,  .
-      .
-<> - ,  .
-
-
-*  :
-
-    [<_>][<>]
-  
-
-<_> -   ,  .
-      .
-<> - ,      .
-      .  ,   
-    ,   .
-
-      ,  
-  :
-: -  ,
-\ -   .
-
-        ,  ,
-  ,        
-   .
-
-     -  .  
- -      .  ,
-    -    
-.
-
-  '<', '>'    '[', ']'.
-
-
-*  :
-
-:      set
-:    <>
-:        <>
-:        <>
-:    <>
- : 1
-
-:      get
-:    <>
-:      <>. 
- : 1,     , 0 - .
-
-:      prepare
-:    <>
-:        <>
-:      SQL- <>  ,
-                   <>.
-:    <>
- : 1
-
-:      query
-:    <_> <> <1> ... <N>
-:        ,   
-               prepare.    
-                 '?'.    
-              ,    <>.  
-               <>    NUMBER,
-                  . ,
-                 ,   
-              <>   SUPER.<_>.
-
-:       <>   
-              .
- :    .
-
-:      optional
-:        <>
-:    <>,      
-                  0,  -  .
- : 1,   -  , 0 - .
-
-:      image
-:    <_> <_> <> [<_> [<_>]]
-:         <_> ( 
-                 "resource_base") 
-                  ,   
-              <_>.      , 
-                . 
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      copy
-:    <_> <_>
-:       <_>   <_>
-	      ( <_>  
-                "resource_base").
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      write
-:    <_>
-:        <>
-:      <>   <_>.
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      read
-:        <_>
-:       <_>.
-:        , 
-               - .
- : 1   , 0 - .
-
-:      include
-:        <_>
-:         <_>.
-:      .
- :  ,    .
-
-:      !
-:        <>
-:     .
-:    .
- :  ,     .
-
-:      replace
-:    <1> <2>
-:        <>
-:    ,     <1>
-	       <2>.
- :  ,     .
-
-:      xml_escape
-:        <>
-:     <>,    
-              '&', '<', '>', '`', '\'   
-	       XML.
- :  ,     .
-
-:      xml_cdata
-:        <>
-:        XML CDATA.
- :  ,     .
-
- DbReader :     
-,       Java.
-
-
-*   DbReader    
-  "  "   Microsoft Access 
-  XML-    
-
-       "
- " (   ):
-
-biotopelist (code, rangcode, rusname, engname, descript, geobotdescr,
-             contributors, descrdate)
-
-  code - ,  ;
-  rangcode -    ABBCCDDEE,  , BB, CC, DD,  - 
-             ,   ;
-  rusname, engname, descript, geobotdescr -  ;
-  descrdate - .
-
-photos (biotope, photo, authphoto)
-
-  biotope - ,   code  biotopelist;
-  photo -   ;
-  authphoto - .
-
-  xml-     biotopelist, 
-  ,    ,  
-   .
-
-  xml-     
- DbReader ( biotope.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: <%xml_escape get\rangcode%> -->
-<_ show="0">
-  < display="0">
-    <%optional:<_ ="1"
-    file="<%xml_escape image:maps/<%get\rangcode%>.gif maps/<%get\code%>.png png%>"/>%>
-<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
-  </>
-  <><%xml_escape get\rusname%></>
-  <><%xml_escape get\class0%></>
-  <1><%xml_escape get\class1%></1>
-  <2><%xml_escape get\class2%></2>
-  <3><%xml_escape get\class3%></3>
-  <_ rows="3"><%xml_cdata get\descript%></_>
-  <_ rows="3"><%xml_cdata get\geobotdescr%></_>
-  <_><%xml_escape get\contributors%></_>
-</_>
-----------------------------------------------------------------------
-
- SQL-,    
-   rangcode, rusname, class0, class1, class2,
-class3, descript, geobotdescr  contributors ( biotope.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-AND Right(t2.rangcode, 8)  = '00000000'
-AND Right(t1.rangcode, 8) <> '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
-AND Right(t2.rangcode, 6)  = '000000'
-AND Right(t1.rangcode, 6) <> '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
-AND Right(t2.rangcode, 4)  = '0000'
-AND Right(t1.rangcode, 4) <> '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
-AND Right(t2.rangcode, 2)  = '00'
-AND Right(t1.rangcode, 2) <> '00') AS class3
-
-FROM biotopelist AS t1
-WHERE rusname
-----------------------------------------------------------------------
-
- biotope.template   <%include\photo.template%>,
-    ( photo.template):
-
-----------------------------------------------------------------------
-    < num="<%xml_escape get\NUMBER%>"
-       <%optional:="<%xml_escape get\authphoto%>" %>text="" 
-       file="<%xml_escape image:biotopephotos/<%get\photo%> images/<%get\SUPER.code%>_<%get\NUMBER%>.jpg jpg 300 300%>" 
-        big="<%xml_escape copy:biotopephotos/<%get\photo%> images/big/<%get\SUPER.code%>_<%get\NUMBER%>.jpg%>"/>
-----------------------------------------------------------------------
-
- SQL-    
-(photo.sql):
-
-----------------------------------------------------------------------
-SELECT * from photos WHERE biotope = ?
-----------------------------------------------------------------------
-
-,    ( classes.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:class_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  < id="<%get\NUMBER%>"><%xml_escape get\rusname%></>%]%>
-</>
-----------------------------------------------------------------------
-
- SQL- (class.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
-  AND EXISTS
-    (SELECT NULL FROM biotopelist AS t2
-     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-     AND Right(t2.rangcode, 8) <> '00000000')
-----------------------------------------------------------------------
-
-,    ( plants.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:plant_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  <><%xml_escape get\rusname%></>%]%>
-</>
-----------------------------------------------------------------------
-
- SQL- (plant.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 2) <> '00'
-----------------------------------------------------------------------
-
- DbReader      index.template,
-     SQL-  
-:
-
-----------------------------------------------------------------------
-<%!:
-  <%prepare:biotope_sql <%read\biotope.sql%>%>
-  <%prepare:photo_sql <%read\photo.sql%>%>
-  <%prepare:class_sql <%read\class.sql%>%>
-  <%prepare:plant_sql <%read\plant.sql%>%>
-
-  <%query:biotope_sql [%\<%write:<%get\code%>.xml <%include\biotope.template%>%> %]%>
-
-  <%write:classes.xml <%include\classes.template%>%>
-  <%write:plants.xml <%include\plants.template%>%>
-%>
-----------------------------------------------------------------------
-
-  XML-:
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: E04020302 -->
-<_ show="0">
-  < display="0">    
-    < num="1" =" .." text="" 
-                file="images/3_1.jpg" 
-                big="images/big/3_1.jpg"/>
-    < num="2" =" .." text="" 
-                file="images/3_2.jpg" 
-                big="images/big/3_2.jpg"/>
-  </>
-  <>Betula sp. - Avenella flexuosa - Polytrichum commune</>
-  <>E.  </>
-  <1> </1>
-  <2></2>
-  <3></3>
-  <_ rows="3"><![CDATA[       .   5 - 7            ,   ,   ,   .]]></_>
-  <_ rows="3"></_>
-  <_></_>
-</_>
-----------------------------------------------------------------------
-
-
-*   DbReader
-
- DbReader     Java  
-   :
-
-ru.karrc.dbreader.DbReader: c   . 
-,       
- JDBC,    .
-
-ru.karrc.dbreader.TemplateParser:   ,
-    .
-
-ru.karrc.dbreader.Function:  .   
-DbReader   .
-
-ru.karrc.dbreader.FunctionDataParser:  
-.      , 
-.       
- TemplateParser.
-
-ru.karrc.dbreader.Functions:     
- ,  Function.
-
-ru.karrc.dbreader.TemplateParser: ,   
-    .
-
- ,    , 
-    .
-
-
-*    .
-
-   : 8,
- Java- ( ): 27,
-    : 1311,
-   (    ): 812,
-  : 180.
-
-
-* 
-
-     DbReader, 
-      
-      .   
-DbReader   "  " 
-  XML-    .
--- a/dist/biotopes/doc/readme.html	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
-          "DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<head>
-  <title>DbReader</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=koi8-r" />
-</head>
-
-<body>
-<h1>DbReader</h1>
-
-<h2> </h2>
-<ol>
-<li> .</li>
-
-<li>  :<br />
- Windows 2000:  //  (ODBC),  biotopes-data.mdb.</li>
-
-<li> :<br />
-  dbreader.properties  ,   :<br />
-    <code>resource: jdbc:odbc:<em></em></code><br />
-   ,       ( biotopephotos  maps):<br />
-    <code>resource_base: C:\\biotopes</code><br />
-( '\'  )</li>
-
-<li> :<br />
-<code>dbreader.bat</code> <br />
-<code>java -jar dbreader.jar</code>.<br />
-   Java Runtime Environment (JRE) 5.0,    <a href="http://java.sun.com/j2se/1.5.0/download.jsp">http://java.sun.com/j2se/1.5.0/download.jsp</a>. ,       JRE -  .
-
-        :<br />
- <code>java -Dru.karrc.dbreader.<em></em>=<em></em> -jar dbreader.jar</code><br />
-(: <code>java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar</code>)</li>
-</ol>
-
-<h2> </h2>
-
-<p>   biotope.sql ( ,   dbreader.properties),     biotope.template,         .</p>
-<p> : <code>&lt;%<em>_</em>[:|\]<em></em>%&gt;</code>, <em> </em> -    ,    .    ,     .</p>
-
-<h3>     </h3>
-
-<ul>
-  <li><code>:</code> -    .</li>
-  <li><code>\</code> -      .       <code>&lt;%</code>  <code>%&gt;</code> (<code>&lt;%\&lt;%%&gt;</code>  <code>&lt;%\%%&gt;&gt;</code>).</li>
-</ul>
-
-<h3></h3>
-
-<ul>
-  <li><code>&lt;%get:<em></em>%&gt;</code><br />
-         .</li>
-
-  <li><code>&lt;%optional:<em></em>%&gt;</code><br />
-    <em></em>,     ,    ,     .</li>
-
-  <li><code>&lt;%escape:<em></em>%&gt;</code><br />
-     ""  (     ).</li>
-
-  <li><code>&lt;%invoke:<em></em>; <em>1</em> <em>2</em> ... <em>N</em>%&gt;</code><br />
-      <em></em>.sql       <em></em>.template.        '?'.</li>
-
-  <li><code>&lt;%image:<em>_</em> <em>_</em> <em></em> [<em>_</em>] [<em>_</em>]%&gt;</code><br />
-      <em>_</em> (     "resource_base")      ,    <em>_</em>.      ,    .
-  ,  <code>&lt;%image:...%&gt;</code>     <em>_</em>,  -   .</li>
-
-  <li><code>&lt;%copy:<em>_</em> <em>_</em>%&gt;</code><br />
-    <em>_</em> (     "resource_base")  <em>_</em>.   ,  <code>&lt;%copy:...%&gt;</code>     <em>_</em>,  -   .</li>
-</ul>
-
-<h3> </h3>
-
-<p><code>NUMBER</code> -      .
-    ,          invoke,      <code>SUPER.</code> (: <code>SUPER.NUMBER</code>).</p>
-
-<h3></h3>
-
-<p>      name      :</p>
-<p><code>&lt;%escape get:name%&gt;</code></p>
-
-<p align="right"><i><a href="mailto:kryshen@cs.karelia.ru"> </a></i></p>
-
-</body>
-</html>
--- a/dist/biotopes/doc/readme.txt	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-text/plain; charset=UTF-8
-
-Установка программы
--------------------
-
-1. Распаковать архив.
-
-2. Установить источник данных:
-в Windows 2000: Панель управления/Администрирование/Источники данных (ODBC), добавить biotopes.mdb.
-
-3. Настроить программу:
-В файле dbreader.properties указать имя, присвоенное источнику данных:
-    "resource: jdbc:odbc:<имя>"
-и путь к каталогу, относительно которого программа будет искать изображения (содержащий biotopephotos и maps):
-    "resource_base: C:\\biotopes"
-(символ '\' необходимо дублировать)
-
-4. Запуск программы: dbreader.bat или "java -jar dbreader.jar"
-Для запуска необходима Java Runtime Environment (JRE) 5.0, можно скачать с http://java.sun.com/j2se/1.5.0/download.jsp. Возможно, будет работать и со старыми версиями JRE - не проверял.
-
-Все значения файла конфигурации можно переопределять при запуске программы:
-    команда "java -Dru.karrc.dbreader.<ключ>=<значение> -jar dbreader.jar"
-(например: "java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar")
-
-
-Работа программы
-----------------
-
-Программа выполняет запрос biotope.sql (или другой, указанный в dbreader.properties), после чего обрабатывает файл biotope.template, заменяя найденные в нем инструкции на результаты их выполнения.
-
-Формат инструкций: "<%список_функций[:|\]данные%>", список функций - имена функций разделенные пробелом, список может быть пустым. Если указано несколько функций, они выполняются начиная с последней.
-
-Разделители между списком функций и данными:
-':' - данные будут обрабатываться рекурсивно.
-'\' - данные будут переданы функции без обработки. Можно использовать для экранирования комбинаций символов "<%" и "%>" ("<%\<%%>" и "<%\%%>>").
-
-Функции:
-<%get:<имя>%> - заменяется на значение встроенной переменной или колонки запроса.
-
-<%optional:<текст>%>
-Заменяется на <текст>, если в нем найдены инструкции, заменившиеся на непустые строки, иначе заменяется на пустую строку.
-
-<%escape:<текст>%>
-Заменяет в тексте "опасные" символы (таблица замен задается в файле конфигурации).
-
-<%invoke:<имя> <парам1> <парам2> ... <парамN>%>
-Выполняет запрос с параметрами <имя>.sql и заменяется на результаты обработки шаблона <имя>.template. Значения параметров подставляются в запрос вместо символа '?'.
-
-<%image:<исх_файл> <кон_файл> <формат> [<макс_ширина>] [<макс_высота>]%>
-Загружает изображение из файла <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base") и преобразует его в указанный формат, сохраняя результат в <кон_файл>. Если заданы максимальная высота и ширина, большие изображения будут уменьшены.
-При успешном выполнении, инструкция "<%!image ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
-
-<%copy:<исх_файл> <кон_файл>%>
-Копирует файл <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base").
-При успешном выполнении, инструкция "<%!copy ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
-
-Встроенные переменные:
-NUMBER - порядковый номер строки результата выполнения запроса.
-Чтобы обратиться к переменной запроса, из которого обрабатываемый шаблон был вызван с помощью функции invoke, перед именем переменной нужно добавить "SUPER." (например: "SUPER.NUMBER").
-
-Пример:
-Следующая инструкция получит значение из столбца name и заменит в нем специальные символы:
-<%escape get:name%>
--- a/dist/biotopes/main.template	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-<%!:
-  <%prepare:biotope_sql <%read\biotope.sql%>%>
-  <%prepare:photo_sql <%read\photo.sql%>%>
-  <%prepare:class_sql <%read\class.sql%>%>
-  <%prepare:plant_sql <%read\plant.sql%>%>
-
-  <%query:biotope_sql [%\<%write:<%db\code%>.xml <%include\biotope.template%>%> %]%>
-
-  <%write:classes.xml <%include\classes.template%>%>
-  <%write:plants.xml <%include\plants.template%>%>
-%>
--- a/dist/biotopes/photo.sql	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-SELECT * from photos WHERE biotope = ?
--- a/dist/biotopes/photo.template	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-    < num="<%number\%>" <%optional:="<%xml_escape db\authphoto%>" %>text="" 
-                file="<%xml_escape image:biotopephotos/<%db\photo%> images/<%super.db\code%>_<%number\%>.jpg jpg 300 300%>" 
-                big="<%xml_escape copy:biotopephotos/<%db\photo%> images/big/<%super.db\code%>_<%number\%>.jpg%>"/>
--- a/dist/biotopes/plant.sql	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 2) <> '00'
--- a/dist/biotopes/plants.template	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:plant_sql [%\
-  <!-- rangcode: <%xml_escape db\rangcode%> -->
-  <><%xml_escape db\rusname%></>%]%>
-</>
--- a/dist/biotopes/tema.properties	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# Data source configuration
-resource          : jdbc:odbc:biotopes-data
-driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-resource_base     : C:\\biotopes\\db
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-input_encoding    : ISO-8859-5
-output_encoding   : ISO-8859-5
-
-# Cache templates
-cache_read        : true
-
-# Output main_template parsing result to stderr
-#output            : stderr
-
-# File to output error messages (redirect stderr)
-#log               : tema.log
Binary file dist/lib/commons-cli-1.1.jar has changed
--- a/dist/tema.properties	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# Data source configuration
-# resource          : jdbc:odbc:biotopes-data
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-# resource_base     : C:\\biotopes\\db
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-# input_encoding    : UTF-8
-# output_encoding   : UTF-8
-
-# Cache templates
-# cache_read        : true
-
-# Output main_template parsing result to stderr
-# output            : stderr
-
-# File to output error messages (redirect stderr)
-# log               : tema.log
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/.cvsignore	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,1 @@
+api
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/manual/.cvsignore	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,1 @@
+manual.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/manual/document.tema	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,107 @@
+[%\
+  Macro definitions used in manual.tema to generate HTML document.
+%]
+
+[%!\
+  Create HTML document.
+  Arguments are read in pairs (name value) until "content" name is found.
+  Then follows the document content.
+
+  TODO: generate TOC.
+%]
+<%define#document
+  <%silent while#
+    <%if:<%not equal:<%set:arg <%next_arg:%>%> content%>
+      <%export:<%arg:%> <%next_arg:%>%>
+    %>
+  %>\\
+<%if:<%not embedded:%>
+<!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" xml:lang="en" lang="en">
+
+<head>
+  <title><%html_escape title:%></title>
+  <meta http-equiv="Content-Type" content="text/html; charset=<%charset:%>" />
+  <meta name="generator" content="<%tema:%>" />
+</head>
+
+<body>
+<h1><%html_escape title:%><%optional:<br /><%html_escape subtitle:%>%></h1>
+%>\
+<%html_escape data:%>
+<%if:<%not embedded:%>
+</body>
+</html>
+%>\
+%>
+
+<%set:charset iso-8859-1%>
+<%set:escape <%char:0%>%>
+
+[%!\
+  Create HTML tag.
+%]
+<%define#tag <%false:<%escape:%><<%data:%><%escape:%>>%>%>
+
+[%!\
+  Escape special HTML characters (except those preceded with <%escape:%>).
+%]
+<%define#html_escape
+  <%replace:<%escape:%> <%:%>
+  <%regex_replace_all:(?<!<%escape:%>)-- &ndash;
+  <%regex_replace_all:(?<!<%escape:%>)--- &mdash;
+  <%regex_replace_all:(?<!<%escape:%>)< &lt;
+  <%regex_replace_all:(?<!<%escape:%>)> &gt;
+  <%regex_replace_all:(?<!<%escape:%>)& &amp;
+    <%data:%>\\
+  %>%>%>%>%>%>\
+%>
+
+<%define#section
+  <%tag:h2<%if:<%first_section:%> <%: class="first"%>%>%><%data:%><%tag:/h2%>\\
+  <%unset:first_section%>\\
+%>
+
+<%define#subsection <%tag:h3%><%data:%><%tag:/h3%>%>
+
+<%define#par <%tag:p%><%data:%><%tag:/p%>%>
+
+<%define#itemize
+<%tag:ul%>
+<%while#\
+  <%tag:li%><%next_arg:%><%tag:/li%>
+%>\
+<%tag:/ul%>
+%>
+
+<%define#code <%tag:code%><%replace:-- <%escape:%>-- <%data:%>%><%tag:/code%>%>
+
+<%define#emph <%tag:i%><%data:%><%tag:/i%>%>
+
+<%define#link
+  <%silent set:url <%next_arg:%>%>\\
+  <%tag:a href="<%url:%>"%>\\
+    <%if_else:<%has_more_data:%>
+      <%data:%>
+      <%url:%>%>\\
+  <%tag:/a%>\\
+%>
+
+<%define#function
+  <%silent set:name <%next_arg:%>%>\\
+  <%silent while#
+    <%set:<%next_arg:%> <%next_arg:%>%>
+  %>\\
+  <%tag:div%>
+    <%tag:b%><%name:%><%tag:/b%><%tag:br /%>
+  <%tag:/div%>
+  <%optional:<%tag:div style="padding-left:1em; margin-bottom: 0.5em;"%>
+    <%description:%>
+  <%tag:/div%>%>
+  <%tag:div style="padding-left:1em; margin-bottom: 1em;"%>
+    <%optional:<%tag:b%>Arguments:<%tag:/b%> <%arguments:%><%tag:br /%>%>
+    <%optional:<%tag:b%>Data:<%tag:/b%> <%input_data:%><%tag:br /%>%>
+    <%optional:<%tag:b%>Output:<%tag:/b%> <%output:%><%tag:br /%>%>
+  <%tag:/div%>
+%>
--- a/doc/manual/index.html	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,358 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
-          "DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<head>
-  <title>Макропроцессор TEMA</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-</head>
-
-<body>
-<h1>Макропроцессор TEMA</h1>
-
-Макропроцессор TEMA обрабатывает заданные шаблоны текстовых файлов и
-заменяет найденные в них инструкции на результаты их выполнения.
-
-<p><b>Формат инструкций</b></p>
-
-<blockquote>
-  <code>&lt;%<i>список_функций</i>{:|\|`}<i>данные</i>%&gt;</code>  
-</blockquote>
-<p>
-где<br />
-
-<code><i>список_функций</i></code> - список имен функций, разделенных
-    пробелами. Может быть пустым.<br />
-<code><i>данные</i></code> - данные, передаваемые функции.
-</p>
-
-<p><b>Формат данных</b></p>
-
-<blockquote>
-  <code>
-    [<i>список_аргументов</i>][<i>текст</i>]
-  </code>
-</blockquote>
-<p>
-где<br />
-
-<code><i>список_аргументов</i></code> - список аргументов функции, разделенных пробелами.
-    Может быть пустым.<br />
-<code><i>текст</i></code> - текст, передаваемый функции без разбиения на аргументы.
-    Может быть пустым. Количество аргументов, после которого следует
-    текст, зависит от функции.
-</p><p>
-Разделитель между списком функций и данными определяет, как должны
-обрабатываться данные функции:
-</p><p>
-<code>:</code> - рекурсивная обработка,<br />
-<code>\</code> или <code>`</code> - передать без обработки.
-</p><p>
-Если в списке функций задано две и более функции, они выполняются,
-начиная с последней, так что каждая функция получает в качестве данных
-результат выполнения следующей функции.
-</p><p>
-Каждая функция имеет код возврата - целое число. Код возврата
-инструкции - код возврата первой в списке функции. Код возврата,
-получаемый при обработке текста - сумма кодов возврата обработанных
-инструкций (как правило, смысл этого значения - количество инструкций,
-замененных на непустой текст).
-</p><p>
-Кроме скобок '&lt;', '&gt;', можно использовать скобки '[', ']'.
-</p>
-
-
-<h2>Функции</h2>
-
-<p><code><b>set</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>значение</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Устанавливает значение переменной <i>имя</i>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>define</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>шаблон</i></td></tr>
-
-<tr><td>Действие:</td><td>Определяет новую функцию <i>имя</i>, при
-вызове которой обрабатывается <i>шаблон</i>. При обработке доступны
-функции <code>nextarg</code> для получения очередного аргумента
-вызываемой функции и <code>data</code> для получения текста.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>load</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i> <i>имя_класса</i></td></tr>
-
-<tr><td>Действие:</td>
-
-<td>Определяет новую функцию <i>имя</i>. Реализация функции определена
-Java-классом <i>имя_класса</i>, наследующим класс
-<code>kryshen.tema.Function</code>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>prepare</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>запрос</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Подготавливает SQL-запрос <i>запрос</i> для выполнения, записывает
-подготовленный запрос в переменную <i>имя</i>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>query</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя_запроса</i> <i>шаблон</i> <i>парам1</i> ... <i>парамN</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Выполняет запрос с параметрами, подготовленный с помощью функции
-prepare. Значения параметров подставляются в запрос вместо символа
-'?'. Значения полей ответа доступны с помощью функции <code>db</code>,
-как переменные шаблона <i>шаблон</i>. При обработки шаблона также
-определяется переменная <code>number</code>, содержащая номер текущей
-строки ответа.</td></tr>
-
-<tr><td>Результат:</td>
-<td>результат обработки шаблона <i>шаблон</i> для каждой строки
-ответа.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>Количество полученных строк ответа.</td></tr>
-</table>
-
-<p><code><b>optional</b></code></p>
-
-<table>
-<tr><td>Текст:</td>
-<td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td><i>данные</i>, если при обработке данных
-был получен код возврата отличный от 0, иначе - пустая
-строка.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1, если результат - пустая строка, 0 - иначе.</td></tr>
-</table>
-
-<p><code><b>image</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>исх_файл</i> <i>кон_файл</i> <i>формат</i> [<i>макс_ширина</i>
-[<i>макс_высота</i>]]</td></tr>
-
-<tr><td>Действие:</td>
-<td>Загружает изображение из файла <i>исх_файл</i> (путь определяется
-относительно конфигурационного параметра "resource_base") и
-преобразует его в указанный формат, сохраняя результат в
-<i>кон_файл</i>. Если заданы максимальная высота и ширина, большие
-изображения будут уменьшены.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>кон_файл</i> при успешном выполнении, пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1 при успешном выполнении, 0 - иначе.</td></tr>
-</table>
-
-
-<p><code><b>copy</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>исх_файл</i> <i>кон_файл</i></td></tr>
-
-<tr><td>Действие:</td><td>Копирует файл <i>исх_файл</i> в файл
-<i>кон_файл</i> (путь <i>исх_файл</i> определяется относительно
-конфигурационного параметра "resource_base").</td></tr>
-
-<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
-пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>write</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>имя_файла</i></td></tr>
-
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Действие:</td><td>Записывает <i>данные</i> в файл
-<i>исх_файл</i>.</td></tr>
-
-<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
-пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>read</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
-<tr><td>Действие:</td><td>Читает файл <i>имя_файла</i>.</td></tr>
-<tr><td>Результат:</td><td>прочитанные данные при успешном выполнении,
-  пустая строка - иначе.</td></tr>
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>include</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
-<tr><td>Действие:</td><td>включает шаблон из файла
-<i>имя_файла</i>.</td></tr>
-<tr><td>Результат:</td><td>результат обработки шаблона.</td></tr>
-<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
-шаблона.</td></tr>
-</table>
-
-<p><code><b>!</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Действие:</td><td>нет.</td></tr>
-
-<tr><td>Результат:</td><td>нет.</td></tr>
-
-<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>replace</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>стр1</i> <i>стр2</i></td></tr>
-
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td>данные, с замененными вхождениями подстроки
-<i>стр1</i> на <i>стр2</i>.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>xml_escape</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-<tr><td>Результат:</td><td>текст <i>данные</i>, в котором символы
-'&amp;', '&lt;', '&gt;', '`', '\' заменены на соответствующие сущности
-XML.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>xml_cdata</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td>данные в виде блока XML CDATA.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.  </td></tr>
-</table>
-
-<p>
-Макропроцессор TEMA расширяем: возможно добавление в систему новых
-функций, реализованных в виде классов на языке Java.
-</p>
-
-<h2>Запуск</h2>
-
-<p>java -jar tema.jar [<i>опции</i>]
-</p><p>
-Опции:
-</p>
-<table>
-<tr><td>-d[emo]</td><td>Демонстрационный режим</td></tr>
-<tr><td>-v[ersion]</td><td>Вывод версии</td></tr>
-<tr><td>-h[help] -u[sage]</td><td>Вывод справки</td></tr>
-</table>
-
-<p>
-При запуске читается файл "tema.properties" из текущего каталога.<br />
-Пример файла "tema.properties":
-</p>
-
-<pre>
-# Настройка источника данных
-# resource          : jdbc:odbc:database
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Базовый каталог ресурсов
-# resource_base     : .
-
-# Шаблон, с которого начинается обработка
-main_template     : main.template
-
-# Кодировки файлов
-# input_encoding    : UTF-8
-# output_encoding   : UTF-8
-
-# Кэширование шаблонов
-# cache_read        : true
-
-# Вывод результата разбора шаблона main.template в stderr
-# output            : stderr
-
-# Вывод сообщений об ошибках в файл
-# log               : dbreader.log
-</pre>
-
-</body>
-</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/manual/manual.tema	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,253 @@
+[%!\
+  Tema User Manual.
+%]\\
+
+<%silent include:document.tema%>\\
+
+<%document:
+  title    <%:Tema -- template macro processor%>
+  author   <%:Mikhail Kryshen%>
+  content
+
+<%section:Introduction%>
+
+<%par:Tema is a macro processor and template engine. It reads the input file 
+recursively expanding any macro found in the text.%>
+
+<%par:Distinctive features:%>
+<%itemize:
+  <%:Extensible: implement new functions as Java classes.%>
+  <%:Database access.%>
+  <%:Processes and outputs data as early as possible (function could start 
+     to output data before all it's arguments are read and parsed).%>
+  <%:Apache Ant integration.%>
+%>
+
+<%par:Tema macro has the following syntax:%>
+
+<%par:[%code\<%function_list{:|#|\|`}[escape]text%>[escape]%]%>
+
+<%par:<%code:function_list%> is the list of text-processing functions.
+Functions will be applied to text in reverse order.%>
+
+<%par:<%code:text%> could contain space-separated list of arguments 
+followed by arbitrary data. Number of arguments is determined by the last
+function in the <%code:function_list%>.%>
+
+<%par:<%code\[arg1 arg2...] [data]%>%>
+
+<%par:The text is separated from the list of functions by one of the 
+following characters:%>
+
+<%itemize:
+  <%\':' -- process text recursively.%>
+  <%\'#' -- parse text but do not replace any embedded macro.%>
+  <%\'\', '`' -- do not parse text (ignore any '<%' sequences).%>
+%>
+
+<%par:<%code:escape%> is either '\' or '\\':%>
+
+<%itemize:
+  <%:<%:\%> -- the following newline symbol will be removed;%>
+  <%:<%:\\%> -- the following sequence of the whitespace characters will be removed.%>
+%>
+
+<%par:You could use <%\'[%' and '%]'%> character sequences to denote macro
+as an alternative to [%\'<%' and '%>'%].%>
+
+<%par:Every function returns integer value. The general convention is to 
+return non-zero for successful operation. The value of the macro substitution 
+is the return value of the first function in the function list. The value of
+any Tema text is the sum value of all macro in the text, except the value of
+the text without any macro equals -1.%>
+
+<%par:Internally, Tema function could be represented by any Java object.
+Instances of kryshen.tema.Function and kryshen.tema.Context are handled
+specially. For any other type of object, function output would be the value 
+returned by the method <%code:toString()%>.%>
+
+<%section:Built-in functions%>
+
+<%par emph:Notice: some functions are not yet described in this manual.
+For the complete list of built-in functions refer to the API documentation
+and source code.%>
+
+<%function:tema
+  output  <%:Tema version%>
+%>
+
+<%function:echo  
+  output  <%:function data%>
+%>
+
+<%function:!
+  output      nothing
+  description <%:Ignores the input text (':' separator works as '#').
+    Use for commenting (e.g. <%code\[%!\ comment %]%>).%>
+%>
+
+<%function:silent  
+  output  nothing
+%>
+
+<%function:set
+  arguments   <%:definition name%>
+  input_data  <%:variable value%>
+  output      <%:function data%>
+  description <%:Defines new function as a static variable.%>
+%>
+
+<%function:define
+  arguments   <%:name, code%>
+  description <%:\\
+    Defines new function. 
+    Use <%code:next_arg%>, <%code:data%>, and <%code:has_more_data%> 
+    functions to access function arguments.%>
+  output      <%:name%> 
+%>
+
+[%!\ TODO: next_arg, data, has_more_data %]\\
+
+<%function:export
+  arguments   <%:definition name%>
+  input_data  <%:static variable value (optional)%>
+  output      <%:function data%>
+  description <%:Exports the definition to the global (outermost) context.%>
+%>
+
+<%function:unset
+  arguments   <%:def1, def2, ...%>
+  output      <%:nothing%>
+  description <%:Unsets the definitions.%>
+%>
+
+<%function:invoke
+  arguments   <%:function, arg1, arg2, ..., data%>
+  output      <%:function output%>
+  description <%:Invokes function.%>
+%>
+
+<%function:load
+  arguments   <%:name, class, url1, url2, ...%>
+  output      <%:name%>
+  description <%:Instantiate specified class as a Tema function definition.
+    URL arguments are optional.%>
+%>
+
+<%function:super
+  input_data  <%:code%>
+  output      <%:output generated by the code%>
+  description <%:Evaluates the specified template code in the super-context
+    (i.e. context of the calling function or context executing 
+    the database query).%>
+%>
+
+<%function:replace
+  arguments   <%:s1, s2, data%>
+  description <%:\\
+    Replaces all occurrences of <%code:s1%> with <%code:s2%> in data.%>
+%>
+
+[%!\ TODO: all functions defined in Strings.java %]\\
+
+<%subsection:Input / output%>
+
+<%function:copy
+  arguments   <%:src, dest%>
+  description <%:Copies file <%code:src%> to <%code:dest%>.%>
+  output      <%:dest%> 
+%>
+
+<%function:write
+  arguments   <%:name%>
+  input_data  <%:text%>
+  description <%:Writes text to file.%> 
+  output      <%:name%>
+%>
+
+<%function:read
+  arguments   <%:name%>
+  description <%:Reads file.%> 
+  output      <%:File contents.%>
+%>
+
+<%function:include
+  arguments   <%:name%>
+  description <%:Includes template.%> 
+  output      <%:Result of processing the template.%>
+%>
+
+<%function:file
+  arguments   <%:base, name%>
+  output      <%:Path constructed from the base directory and file name.%> 
+%>
+
+<%subsection:Conditionals%>
+
+<%function:optional
+  input_data  <%:text%>
+  output      <%:Function data if it's value is non-zero or empty text.%>
+%>
+
+<%function:while
+  input_data  <%:code%>
+  description <%:Repeatedly outputs it's evaluated data while it has non-zero value.%>
+%>
+
+[%!\ TODO: all functions defined in Control.java %]\\
+
+<%subsection:Database%>
+
+<%function:db_connect
+  arguments   <%:connection name%>
+  input_data  <%:resource%>
+  description <%:Establishes connection with the database.
+    Load the appropriate database driver using the <%code:load%> function
+    before using <%code:db_connect%> 
+    (e.g. [%code\<%load:driver sun.jdbc.odbc.JdbcOdbcDriver%>%]).%>
+%>
+
+<%function:db_prepare
+  arguments   <%:query name, connection name%>
+  input_data  <%:SQL statement%>
+%>
+
+<%function:db_query
+  arguments   <%:query, template, arg1, arg2, ...%>
+  description <%:Executes query, evaluating the template for each result row.%>
+%>
+
+<%function:db
+  input_data  <%:column name%>
+  output      <%:value from the query result.%>
+  description <%:Available in the template for the <%code:db_query%> function.%>  
+%>
+
+<%function:db_row
+  output      <%:current row number in the result set.%>
+  description <%:Available in the template for the <%code:db_query%> function.%>  
+%>
+
+[%!\ TODO: all functions defined in ImageConverter.java %]\\
+
+<%section:Running Tema%>
+
+<%par:Change to the dist subdirectory in the distribution package and
+issue the following command:%>
+
+<%par:<%code:java -jar tema.jar [options] [files]%>%>
+
+<%par:The following options are recognized:%>
+<%itemize:
+  <%:<%code:--demo%> --- run demo console with a code example;%>
+  <%:<%code:-h, --help%> --- print help message;%>
+  <%:<%code:--input-encoding <arg>%> --- set the input encoding;%>
+  <%:<%code:--log <arg>%> --- log all error messages to the specified file;%>
+  <%:<%code:-o, --output <arg>%> --- set the output file;%>
+  <%:<%code:--output-encoding <arg>%> --- set the output encoding;%>
+  <%:<%code:-v, --version%> --- print the version information and exit.%>
+%>
+
+<%section:Homepage%>
+
+<%par link:http://kryshen.pp.ru/tema/%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/biotope-top.sql	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,20 @@
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1) AND
+Right(t2.rangcode, 8) = '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3) AND
+Right(t2.rangcode, 6) = '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5) AND
+Right(t2.rangcode, 4) = '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7) AND
+Right(t2.rangcode, 2) = '00') AS class3
+
+FROM biotopelist AS t1
+WHERE Right(t1.rangcode, 2) <> '00' AND rusname
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/biotope.sql	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,24 @@
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+AND Right(t2.rangcode, 8)  = '00000000'
+AND Right(t1.rangcode, 8) <> '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
+AND Right(t2.rangcode, 6)  = '000000'
+AND Right(t1.rangcode, 6) <> '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
+AND Right(t2.rangcode, 4)  = '0000'
+AND Right(t1.rangcode, 4) <> '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
+AND Right(t2.rangcode, 2)  = '00'
+AND Right(t1.rangcode, 2) <> '00') AS class3
+
+FROM biotopelist AS t1
+WHERE rusname
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/biotope.template	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: <%xml_escape db\rangcode%> -->
+<_ show="0">
+  < display="0">
+    <%optional:<_ ="1" file="<%xml_escape image:maps/<%db\rangcode%>.gif maps/<%db\code%>.png png%>"/>%>
+<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
+  </>
+  <><%xml_escape db\rusname%></>
+  <><%xml_escape db\class0%></>
+  <1><%xml_escape db\class1%></1>
+  <2><%xml_escape db\class2%></2>
+  <3><%xml_escape db\class3%></3>
+  <_ rows="3"><%xml_cdata db\descript%></_>
+  <_ rows="3"><%xml_cdata db\geobotdescr%></_>
+  <_><%xml_escape db\contributors%></_>
+</_>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/brief.dtd	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,27 @@
+<?xml encoding="ISO-8859-5"?>
+<!ELEMENT _ (?, , , 1, 2, 3, _, _, _)>
+<!ELEMENT  (|_)*>
+<!ELEMENT  EMPTY>
+<!ELEMENT _ EMPTY>
+<!ELEMENT  (#PCDATA)>
+<!ELEMENT  (#PCDATA)>
+<!ELEMENT 1 (#PCDATA)>
+<!ELEMENT 2 (#PCDATA)>
+<!ELEMENT 3 (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ATTLIST _ show (0|1) "1"> 
+<!ATTLIST 
+	display CDATA #FIXED "0">
+<!ATTLIST 
+	num ID #REQUIRED
+	file CDATA #REQUIRED
+	big CDATA #REQUIRED
+	text CDATA #IMPLIED
+	 CDATA #IMPLIED>
+<!ATTLIST _
+	num ID #REQUIRED
+	file CDATA #REQUIRED>
+<!ATTLIST _ rows CDATA #FIXED "3">
+<!ATTLIST _ rows CDATA #FIXED "3">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/class.sql	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,7 @@
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
+  AND EXISTS
+    (SELECT NULL FROM biotopelist AS t2
+     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+     AND Right(t2.rangcode, 8) <> '00000000')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/classes.template	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:class_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  < id="<%get\NUMBER%>"><%xml_escape db\rusname%></>%]%>
+</>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/doc/article.txt	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,391 @@
+*     XML-  .
+
+      " 
+"   Microsoft Access  XML- 
+       DbReader.
+
+ DbReader     
+      .  
+        
+    .   
+ SQL-,     
+.
+
+
+*  :
+
+    <%<_>{:|\}<>%>
+   
+
+<_> -   ,  .
+      .
+<> - ,  .
+
+
+*  :
+
+    [<_>][<>]
+  
+
+<_> -   ,  .
+      .
+<> - ,      .
+      .  ,   
+    ,   .
+
+      ,  
+  :
+: -  ,
+\ -   .
+
+        ,  ,
+  ,        
+   .
+
+     -  .  
+ -      .  ,
+    -    
+.
+
+  '<', '>'    '[', ']'.
+
+
+*  :
+
+:      set
+:    <>
+:        <>
+:        <>
+:    <>
+ : 1
+
+:      get
+:    <>
+:      <>. 
+ : 1,     , 0 - .
+
+:      prepare
+:    <>
+:        <>
+:      SQL- <>  ,
+                   <>.
+:    <>
+ : 1
+
+:      query
+:    <_> <> <1> ... <N>
+:        ,   
+               prepare.    
+                 '?'.    
+              ,    <>.  
+               <>    NUMBER,
+                  . ,
+                 ,   
+              <>   SUPER.<_>.
+
+:       <>   
+              .
+ :    .
+
+:      optional
+:        <>
+:    <>,      
+                  0,  -  .
+ : 1,   -  , 0 - .
+
+:      image
+:    <_> <_> <> [<_> [<_>]]
+:         <_> ( 
+                 "resource_base") 
+                  ,   
+              <_>.      , 
+                . 
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      copy
+:    <_> <_>
+:       <_>   <_>
+	      ( <_>  
+                "resource_base").
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      write
+:    <_>
+:        <>
+:      <>   <_>.
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      read
+:        <_>
+:       <_>.
+:        , 
+               - .
+ : 1   , 0 - .
+
+:      include
+:        <_>
+:         <_>.
+:      .
+ :  ,    .
+
+:      !
+:        <>
+:     .
+:    .
+ :  ,     .
+
+:      replace
+:    <1> <2>
+:        <>
+:    ,     <1>
+	       <2>.
+ :  ,     .
+
+:      xml_escape
+:        <>
+:     <>,    
+              '&', '<', '>', '`', '\'   
+	       XML.
+ :  ,     .
+
+:      xml_cdata
+:        <>
+:        XML CDATA.
+ :  ,     .
+
+ DbReader :     
+,       Java.
+
+
+*   DbReader    
+  "  "   Microsoft Access 
+  XML-    
+
+       "
+ " (   ):
+
+biotopelist (code, rangcode, rusname, engname, descript, geobotdescr,
+             contributors, descrdate)
+
+  code - ,  ;
+  rangcode -    ABBCCDDEE,  , BB, CC, DD,  - 
+             ,   ;
+  rusname, engname, descript, geobotdescr -  ;
+  descrdate - .
+
+photos (biotope, photo, authphoto)
+
+  biotope - ,   code  biotopelist;
+  photo -   ;
+  authphoto - .
+
+  xml-     biotopelist, 
+  ,    ,  
+   .
+
+  xml-     
+ DbReader ( biotope.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: <%xml_escape get\rangcode%> -->
+<_ show="0">
+  < display="0">
+    <%optional:<_ ="1"
+    file="<%xml_escape image:maps/<%get\rangcode%>.gif maps/<%get\code%>.png png%>"/>%>
+<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
+  </>
+  <><%xml_escape get\rusname%></>
+  <><%xml_escape get\class0%></>
+  <1><%xml_escape get\class1%></1>
+  <2><%xml_escape get\class2%></2>
+  <3><%xml_escape get\class3%></3>
+  <_ rows="3"><%xml_cdata get\descript%></_>
+  <_ rows="3"><%xml_cdata get\geobotdescr%></_>
+  <_><%xml_escape get\contributors%></_>
+</_>
+----------------------------------------------------------------------
+
+ SQL-,    
+   rangcode, rusname, class0, class1, class2,
+class3, descript, geobotdescr  contributors ( biotope.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+AND Right(t2.rangcode, 8)  = '00000000'
+AND Right(t1.rangcode, 8) <> '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
+AND Right(t2.rangcode, 6)  = '000000'
+AND Right(t1.rangcode, 6) <> '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
+AND Right(t2.rangcode, 4)  = '0000'
+AND Right(t1.rangcode, 4) <> '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
+AND Right(t2.rangcode, 2)  = '00'
+AND Right(t1.rangcode, 2) <> '00') AS class3
+
+FROM biotopelist AS t1
+WHERE rusname
+----------------------------------------------------------------------
+
+ biotope.template   <%include\photo.template%>,
+    ( photo.template):
+
+----------------------------------------------------------------------
+    < num="<%xml_escape get\NUMBER%>"
+       <%optional:="<%xml_escape get\authphoto%>" %>text="" 
+       file="<%xml_escape image:biotopephotos/<%get\photo%> images/<%get\SUPER.code%>_<%get\NUMBER%>.jpg jpg 300 300%>" 
+        big="<%xml_escape copy:biotopephotos/<%get\photo%> images/big/<%get\SUPER.code%>_<%get\NUMBER%>.jpg%>"/>
+----------------------------------------------------------------------
+
+ SQL-    
+(photo.sql):
+
+----------------------------------------------------------------------
+SELECT * from photos WHERE biotope = ?
+----------------------------------------------------------------------
+
+,    ( classes.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:class_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  < id="<%get\NUMBER%>"><%xml_escape get\rusname%></>%]%>
+</>
+----------------------------------------------------------------------
+
+ SQL- (class.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
+  AND EXISTS
+    (SELECT NULL FROM biotopelist AS t2
+     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+     AND Right(t2.rangcode, 8) <> '00000000')
+----------------------------------------------------------------------
+
+,    ( plants.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:plant_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  <><%xml_escape get\rusname%></>%]%>
+</>
+----------------------------------------------------------------------
+
+ SQL- (plant.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 2) <> '00'
+----------------------------------------------------------------------
+
+ DbReader      index.template,
+     SQL-  
+:
+
+----------------------------------------------------------------------
+<%!:
+  <%prepare:biotope_sql <%read\biotope.sql%>%>
+  <%prepare:photo_sql <%read\photo.sql%>%>
+  <%prepare:class_sql <%read\class.sql%>%>
+  <%prepare:plant_sql <%read\plant.sql%>%>
+
+  <%query:biotope_sql [%\<%write:<%get\code%>.xml <%include\biotope.template%>%> %]%>
+
+  <%write:classes.xml <%include\classes.template%>%>
+  <%write:plants.xml <%include\plants.template%>%>
+%>
+----------------------------------------------------------------------
+
+  XML-:
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: E04020302 -->
+<_ show="0">
+  < display="0">    
+    < num="1" =" .." text="" 
+                file="images/3_1.jpg" 
+                big="images/big/3_1.jpg"/>
+    < num="2" =" .." text="" 
+                file="images/3_2.jpg" 
+                big="images/big/3_2.jpg"/>
+  </>
+  <>Betula sp. - Avenella flexuosa - Polytrichum commune</>
+  <>E.  </>
+  <1> </1>
+  <2></2>
+  <3></3>
+  <_ rows="3"><![CDATA[       .   5 - 7            ,   ,   ,   .]]></_>
+  <_ rows="3"></_>
+  <_></_>
+</_>
+----------------------------------------------------------------------
+
+
+*   DbReader
+
+ DbReader     Java  
+   :
+
+ru.karrc.dbreader.DbReader: c   . 
+,       
+ JDBC,    .
+
+ru.karrc.dbreader.TemplateParser:   ,
+    .
+
+ru.karrc.dbreader.Function:  .   
+DbReader   .
+
+ru.karrc.dbreader.FunctionDataParser:  
+.      , 
+.       
+ TemplateParser.
+
+ru.karrc.dbreader.Functions:     
+ ,  Function.
+
+ru.karrc.dbreader.TemplateParser: ,   
+    .
+
+ ,    , 
+    .
+
+
+*    .
+
+   : 8,
+ Java- ( ): 27,
+    : 1311,
+   (    ): 812,
+  : 180.
+
+
+* 
+
+     DbReader, 
+      
+      .   
+DbReader   "  " 
+  XML-    .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/doc/readme.html	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,86 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+          "DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+  <title>DbReader</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=koi8-r" />
+</head>
+
+<body>
+<h1>DbReader</h1>
+
+<h2> </h2>
+<ol>
+<li> .</li>
+
+<li>  :<br />
+ Windows 2000:  //  (ODBC),  biotopes-data.mdb.</li>
+
+<li> :<br />
+  dbreader.properties  ,   :<br />
+    <code>resource: jdbc:odbc:<em></em></code><br />
+   ,       ( biotopephotos  maps):<br />
+    <code>resource_base: C:\\biotopes</code><br />
+( '\'  )</li>
+
+<li> :<br />
+<code>dbreader.bat</code> <br />
+<code>java -jar dbreader.jar</code>.<br />
+   Java Runtime Environment (JRE) 5.0,    <a href="http://java.sun.com/j2se/1.5.0/download.jsp">http://java.sun.com/j2se/1.5.0/download.jsp</a>. ,       JRE -  .
+
+        :<br />
+ <code>java -Dru.karrc.dbreader.<em></em>=<em></em> -jar dbreader.jar</code><br />
+(: <code>java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar</code>)</li>
+</ol>
+
+<h2> </h2>
+
+<p>   biotope.sql ( ,   dbreader.properties),     biotope.template,         .</p>
+<p> : <code>&lt;%<em>_</em>[:|\]<em></em>%&gt;</code>, <em> </em> -    ,    .    ,     .</p>
+
+<h3>     </h3>
+
+<ul>
+  <li><code>:</code> -    .</li>
+  <li><code>\</code> -      .       <code>&lt;%</code>  <code>%&gt;</code> (<code>&lt;%\&lt;%%&gt;</code>  <code>&lt;%\%%&gt;&gt;</code>).</li>
+</ul>
+
+<h3></h3>
+
+<ul>
+  <li><code>&lt;%get:<em></em>%&gt;</code><br />
+         .</li>
+
+  <li><code>&lt;%optional:<em></em>%&gt;</code><br />
+    <em></em>,     ,    ,     .</li>
+
+  <li><code>&lt;%escape:<em></em>%&gt;</code><br />
+     ""  (     ).</li>
+
+  <li><code>&lt;%invoke:<em></em>; <em>1</em> <em>2</em> ... <em>N</em>%&gt;</code><br />
+      <em></em>.sql       <em></em>.template.        '?'.</li>
+
+  <li><code>&lt;%image:<em>_</em> <em>_</em> <em></em> [<em>_</em>] [<em>_</em>]%&gt;</code><br />
+      <em>_</em> (     "resource_base")      ,    <em>_</em>.      ,    .
+  ,  <code>&lt;%image:...%&gt;</code>     <em>_</em>,  -   .</li>
+
+  <li><code>&lt;%copy:<em>_</em> <em>_</em>%&gt;</code><br />
+    <em>_</em> (     "resource_base")  <em>_</em>.   ,  <code>&lt;%copy:...%&gt;</code>     <em>_</em>,  -   .</li>
+</ul>
+
+<h3> </h3>
+
+<p><code>NUMBER</code> -      .
+    ,          invoke,      <code>SUPER.</code> (: <code>SUPER.NUMBER</code>).</p>
+
+<h3></h3>
+
+<p>      name      :</p>
+<p><code>&lt;%escape get:name%&gt;</code></p>
+
+<p align="right"><i><a href="mailto:kryshen@cs.karelia.ru"> </a></i></p>
+
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/doc/readme.txt	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,63 @@
+text/plain; charset=UTF-8
+
+Установка программы
+-------------------
+
+1. Распаковать архив.
+
+2. Установить источник данных:
+в Windows 2000: Панель управления/Администрирование/Источники данных (ODBC), добавить biotopes.mdb.
+
+3. Настроить программу:
+В файле dbreader.properties указать имя, присвоенное источнику данных:
+    "resource: jdbc:odbc:<имя>"
+и путь к каталогу, относительно которого программа будет искать изображения (содержащий biotopephotos и maps):
+    "resource_base: C:\\biotopes"
+(символ '\' необходимо дублировать)
+
+4. Запуск программы: dbreader.bat или "java -jar dbreader.jar"
+Для запуска необходима Java Runtime Environment (JRE) 5.0, можно скачать с http://java.sun.com/j2se/1.5.0/download.jsp. Возможно, будет работать и со старыми версиями JRE - не проверял.
+
+Все значения файла конфигурации можно переопределять при запуске программы:
+    команда "java -Dru.karrc.dbreader.<ключ>=<значение> -jar dbreader.jar"
+(например: "java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar")
+
+
+Работа программы
+----------------
+
+Программа выполняет запрос biotope.sql (или другой, указанный в dbreader.properties), после чего обрабатывает файл biotope.template, заменяя найденные в нем инструкции на результаты их выполнения.
+
+Формат инструкций: "<%список_функций[:|\]данные%>", список функций - имена функций разделенные пробелом, список может быть пустым. Если указано несколько функций, они выполняются начиная с последней.
+
+Разделители между списком функций и данными:
+':' - данные будут обрабатываться рекурсивно.
+'\' - данные будут переданы функции без обработки. Можно использовать для экранирования комбинаций символов "<%" и "%>" ("<%\<%%>" и "<%\%%>>").
+
+Функции:
+<%get:<имя>%> - заменяется на значение встроенной переменной или колонки запроса.
+
+<%optional:<текст>%>
+Заменяется на <текст>, если в нем найдены инструкции, заменившиеся на непустые строки, иначе заменяется на пустую строку.
+
+<%escape:<текст>%>
+Заменяет в тексте "опасные" символы (таблица замен задается в файле конфигурации).
+
+<%invoke:<имя> <парам1> <парам2> ... <парамN>%>
+Выполняет запрос с параметрами <имя>.sql и заменяется на результаты обработки шаблона <имя>.template. Значения параметров подставляются в запрос вместо символа '?'.
+
+<%image:<исх_файл> <кон_файл> <формат> [<макс_ширина>] [<макс_высота>]%>
+Загружает изображение из файла <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base") и преобразует его в указанный формат, сохраняя результат в <кон_файл>. Если заданы максимальная высота и ширина, большие изображения будут уменьшены.
+При успешном выполнении, инструкция "<%!image ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
+
+<%copy:<исх_файл> <кон_файл>%>
+Копирует файл <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base").
+При успешном выполнении, инструкция "<%!copy ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
+
+Встроенные переменные:
+NUMBER - порядковый номер строки результата выполнения запроса.
+Чтобы обратиться к переменной запроса, из которого обрабатываемый шаблон был вызван с помощью функции invoke, перед именем переменной нужно добавить "SUPER." (например: "SUPER.NUMBER").
+
+Пример:
+Следующая инструкция получит значение из столбца name и заменит в нем специальные символы:
+<%escape get:name%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/main.template	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,11 @@
+<%!:
+  <%prepare:biotope_sql <%read\biotope.sql%>%>
+  <%prepare:photo_sql <%read\photo.sql%>%>
+  <%prepare:class_sql <%read\class.sql%>%>
+  <%prepare:plant_sql <%read\plant.sql%>%>
+
+  <%query:biotope_sql [%\<%write:<%db\code%>.xml <%include\biotope.template%>%> %]%>
+
+  <%write:classes.xml <%include\classes.template%>%>
+  <%write:plants.xml <%include\plants.template%>%>
+%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/photo.sql	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,1 @@
+SELECT * from photos WHERE biotope = ?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/photo.template	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,3 @@
+    < num="<%number\%>" <%optional:="<%xml_escape db\authphoto%>" %>text="" 
+                file="<%xml_escape image:biotopephotos/<%db\photo%> images/<%super.db\code%>_<%number\%>.jpg jpg 300 300%>" 
+                big="<%xml_escape copy:biotopephotos/<%db\photo%> images/big/<%super.db\code%>_<%number\%>.jpg%>"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/plant.sql	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,3 @@
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 2) <> '00'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/plants.template	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:plant_sql [%\
+  <!-- rangcode: <%xml_escape db\rangcode%> -->
+  <><%xml_escape db\rusname%></>%]%>
+</>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/biotopes/tema.properties	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,22 @@
+# Data source configuration
+resource          : jdbc:odbc:biotopes-data
+driver            : sun.jdbc.odbc.JdbcOdbcDriver
+
+# Base directory for images and files
+resource_base     : C:\\biotopes\\db
+
+# Template to start processing with
+main_template     : main.template
+
+# File encodings
+input_encoding    : ISO-8859-5
+output_encoding   : ISO-8859-5
+
+# Cache templates
+cache_read        : true
+
+# Output main_template parsing result to stderr
+#output            : stderr
+
+# File to output error messages (redirect stderr)
+#log               : tema.log
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/manual-ru-old/index.html	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,370 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+          "DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+  <title>Макропроцессор TEMA</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+
+<body>
+<h1>Макропроцессор TEMA</h1>
+
+<p>Макропроцессор TEMA обрабатывает заданные шаблоны текстовых файлов и
+заменяет найденные в них инструкции на результаты их выполнения.
+</p><p>
+Особенности:
+</p>
+<ul>
+  <li>Расширяемость: возможно динамически подключать новые функии,
+  реализованные в виде классов на Java.</li>
+  <li>Работа с базами данных: использование SQL-запросов для получения
+  данных.</li>
+  <li>Поточная обработка данных: инструкции обрабатываются по мере
+  чтения текста без необходимости загрузки файла в оперативную память
+  и выполнения полного синтаксического разбора.</li>
+</ul>
+
+<p><b>Формат инструкций</b></p>
+
+<blockquote>
+  <code>&lt;%<i>список_функций</i>{:|\|`}<i>данные</i>%&gt;</code>  
+</blockquote>
+<p>
+где<br />
+
+<code><i>список_функций</i></code> - список имен функций, разделенных
+    пробелами. Может быть пустым.<br />
+<code><i>данные</i></code> - данные, передаваемые функции.
+</p>
+
+<p><b>Формат данных</b></p>
+
+<blockquote>
+  <code>
+    [<i>список_аргументов</i>][<i>текст</i>]
+  </code>
+</blockquote>
+<p>
+где<br />
+
+<code><i>список_аргументов</i></code> - список аргументов функции, разделенных пробелами.
+    Может быть пустым.<br />
+<code><i>текст</i></code> - текст, передаваемый функции без разбиения на аргументы.
+    Может быть пустым. Количество аргументов, после которого следует
+    текст, зависит от функции.
+</p><p>
+Разделитель между списком функций и данными определяет, как должны
+обрабатываться данные функции:
+</p><p>
+<code>:</code> - рекурсивная обработка,<br />
+<code>\</code> или <code>`</code> - передать без обработки.
+</p><p>
+Если в списке функций задано две и более функции, они выполняются,
+начиная с последней, так что каждая функция получает в качестве данных
+результат выполнения следующей функции.
+</p><p>
+Каждая функция имеет код возврата - целое число. Код возврата
+инструкции - код возврата первой в списке функции. Код возврата,
+получаемый при обработке текста - сумма кодов возврата обработанных
+инструкций (как правило, смысл этого значения - количество инструкций,
+замененных на непустой текст).
+</p><p>
+Кроме скобок '&lt;', '&gt;', можно использовать скобки '[', ']'.
+</p>
+
+
+<h2>Функции</h2>
+
+<p><code><b>set</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>значение</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Устанавливает значение переменной <i>имя</i>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>define</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>шаблон</i></td></tr>
+
+<tr><td>Действие:</td><td>Определяет новую функцию <i>имя</i>, при
+вызове которой обрабатывается <i>шаблон</i>. При обработке доступны
+функции <code>nextarg</code> для получения очередного аргумента
+вызываемой функции и <code>data</code> для получения текста.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>load</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i> <i>имя_класса</i></td></tr>
+
+<tr><td>Действие:</td>
+
+<td>Определяет новую функцию <i>имя</i>. Реализация функции определена
+Java-классом <i>имя_класса</i>, наследующим класс
+<code>kryshen.tema.Function</code>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>prepare</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>запрос</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Подготавливает SQL-запрос <i>запрос</i> для выполнения, записывает
+подготовленный запрос в переменную <i>имя</i>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>query</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя_запроса</i> <i>шаблон</i> <i>парам1</i> ... <i>парамN</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Выполняет запрос с параметрами, подготовленный с помощью функции
+prepare. Значения параметров подставляются в запрос вместо символа
+'?'. Значения полей ответа доступны с помощью функции <code>db</code>,
+как переменные шаблона <i>шаблон</i>. При обработки шаблона также
+определяется переменная <code>number</code>, содержащая номер текущей
+строки ответа.</td></tr>
+
+<tr><td>Результат:</td>
+<td>результат обработки шаблона <i>шаблон</i> для каждой строки
+ответа.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>Количество полученных строк ответа.</td></tr>
+</table>
+
+<p><code><b>optional</b></code></p>
+
+<table>
+<tr><td>Текст:</td>
+<td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td><i>данные</i>, если при обработке данных
+был получен код возврата отличный от 0, иначе - пустая
+строка.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1, если результат - пустая строка, 0 - иначе.</td></tr>
+</table>
+
+<p><code><b>image</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>исх_файл</i> <i>кон_файл</i> <i>формат</i> [<i>макс_ширина</i>
+[<i>макс_высота</i>]]</td></tr>
+
+<tr><td>Действие:</td>
+<td>Загружает изображение из файла <i>исх_файл</i> (путь определяется
+относительно конфигурационного параметра "resource_base") и
+преобразует его в указанный формат, сохраняя результат в
+<i>кон_файл</i>. Если заданы максимальная высота и ширина, большие
+изображения будут уменьшены.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>кон_файл</i> при успешном выполнении, пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1 при успешном выполнении, 0 - иначе.</td></tr>
+</table>
+
+
+<p><code><b>copy</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>исх_файл</i> <i>кон_файл</i></td></tr>
+
+<tr><td>Действие:</td><td>Копирует файл <i>исх_файл</i> в файл
+<i>кон_файл</i> (путь <i>исх_файл</i> определяется относительно
+конфигурационного параметра "resource_base").</td></tr>
+
+<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
+пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>write</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>имя_файла</i></td></tr>
+
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Действие:</td><td>Записывает <i>данные</i> в файл
+<i>исх_файл</i>.</td></tr>
+
+<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
+пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>read</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
+<tr><td>Действие:</td><td>Читает файл <i>имя_файла</i>.</td></tr>
+<tr><td>Результат:</td><td>прочитанные данные при успешном выполнении,
+  пустая строка - иначе.</td></tr>
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>include</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
+<tr><td>Действие:</td><td>включает шаблон из файла
+<i>имя_файла</i>.</td></tr>
+<tr><td>Результат:</td><td>результат обработки шаблона.</td></tr>
+<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
+шаблона.</td></tr>
+</table>
+
+<p><code><b>!</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Действие:</td><td>нет.</td></tr>
+
+<tr><td>Результат:</td><td>нет.</td></tr>
+
+<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>replace</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>стр1</i> <i>стр2</i></td></tr>
+
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td>данные, с замененными вхождениями подстроки
+<i>стр1</i> на <i>стр2</i>.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>xml_escape</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+<tr><td>Результат:</td><td>текст <i>данные</i>, в котором символы
+'&amp;', '&lt;', '&gt;', '`', '\' заменены на соответствующие сущности
+XML.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>xml_cdata</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td>данные в виде блока XML CDATA.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.  </td></tr>
+</table>
+
+<p>
+Макропроцессор TEMA расширяем: возможно добавление в систему новых
+функций, реализованных в виде классов на языке Java.
+</p>
+
+<h2>Запуск</h2>
+
+<p>java -jar tema.jar [<i>опции</i>]
+</p><p>
+Опции:
+</p>
+<table>
+<tr><td>-d[emo]</td><td>Демонстрационный режим</td></tr>
+<tr><td>-v[ersion]</td><td>Вывод версии</td></tr>
+<tr><td>-h[help] -u[sage]</td><td>Вывод справки</td></tr>
+</table>
+
+<p>
+При запуске читается файл "tema.properties" из текущего каталога.<br />
+Пример файла "tema.properties":
+</p>
+
+<pre>
+# Настройка источника данных
+# resource          : jdbc:odbc:database
+# driver            : sun.jdbc.odbc.JdbcOdbcDriver
+
+# Базовый каталог ресурсов
+# resource_base     : .
+
+# Шаблон, с которого начинается обработка
+main_template     : main.template
+
+# Кодировки файлов
+# input_encoding    : UTF-8
+# output_encoding   : UTF-8
+
+# Кэширование шаблонов
+# cache_read        : true
+
+# Вывод результата разбора шаблона main.template в stderr
+# output            : stderr
+
+# Вывод сообщений об ошибках в файл
+# log               : dbreader.log
+</pre>
+
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/util/header	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,21 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: $
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/util/header.gpl	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,20 @@
+/*
+ *  Copyright (C) 2006, 2007, 2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema 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.
+ *
+ *  Tema is distributed in the hope that 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 Tema.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: header.gpl,v 1.1 2008/02/16 16:56:34 mikhail Exp $
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/util/update_headers.pl	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+use File::Find;
+
+undef $/;
+
+my $src = "../../src";
+my $header_file = "header";
+
+open (FILE, "$header_file");
+my $header = <FILE>;
+close(FILE);
+
+find (\&update_header, $src);
+
+sub update_header
+{
+   my $code;
+   
+   if ( $File::Find::name !~ /^.*\.java$/ )
+   {
+     return;
+   }
+
+   print $File::Find::name."\n";   
+   open(FILE, "$_") or die "error opening file";
+  
+   $code = <FILE>;
+   $code =~ s|(^[[:space:]]*/\*.*?\*/[[:space:]]*?\n\|^)|$header|s;
+   
+   close(FILE);
+
+   open(FILE, ">$_") or die "error opening file";
+   print FILE $code;
+   close(FILE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nbproject/.cvsignore	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,1 @@
+private
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nbproject/project.xml	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>TEMA</name>
+            <properties/>
+            <folders>
+                <source-folder>
+                    <label>src</label>
+                    <type>java</type>
+                    <location>src</location>
+                </source-folder>
+                <source-folder>
+                    <label>test</label>
+                    <type>java</type>
+                    <location>test</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>compile</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="javadoc">
+                    <target>doc.api</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>compile</target>
+                </action>
+                <action name="run">
+                    <target>run.demo</target>
+                </action>
+            </ide-actions>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src</label>
+                        <location>src</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>test</label>
+                        <location>test</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="javadoc"/>
+                    <ide-action name="run"/>
+                    <action>
+                        <label>Compile manual</label>
+                        <target>doc.manual</target>
+                    </action>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
+            <compilation-unit>
+                <package-root>src</package-root>
+                <classpath mode="compile">build:dist/lib/commons-cli-1.1.jar:../../../../../usr/share/java/ant.jar</classpath>
+                <source-level>1.5</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>test</package-root>
+                <unit-tests/>
+                <classpath mode="compile">build</classpath>
+                <source-level>1.5</source-level>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
--- a/res/kryshen/tema/demo/demo.template	Thu Dec 14 23:22:05 2006 +0300
+++ b/res/kryshen/tema/demo/demo.template	Tue Feb 19 20:32:17 2008 +0300
@@ -1,25 +1,34 @@
-[%!\ TEMA demo template %]
+[%!\
+  Tema demo template 
+%]\\
 
-Вывод текста:
-test text
+<%tema:%>
 
-Вывод специальных символов:
-[%\<%test%>%]
+Escaping special character sequences:
+[%\<%test:%>%]
 
-Объявление функции:
-<%define\test test arg1:[%nextarg:%], test arg2:[%nextarg:%], test data:[%data:%].%>   
+Macro definition:
+<%define#test
+  arg1:<%next_arg:%>, arg2:<%next_arg:%>, data:<%data:%>.%>
 
-Вызов новой функции:
-<%test:1 2 3%>
+Invoke defined macro:
+<%test:1 2 3 4 5%>
 
-Загрузка функции из класса:
+Define macro implemented as a Java class:
 <%load\hello kryshen.tema.demo.Hello%>
 
-Выполенение загруженной функции:
-<%hello\TEMA%>
+Invoke defined macro:
+<%hello\Tema%>
+
+Conditional output:
+<%optional:<%true:%>True%>\
+<%optional:<%false:%>False%>
 
-Условное выполнение:
-<%optional:<%true:%>True%> <%optional:<%false:%>False%>
+Cycle:
+<%silent define#cycle_test
+  Arguments list: <%while#"<%next_arg:%>" %>\
+%>\
+<%cycle_test:1 2 3 <%:4 5%>%>
 
-Тестирование xml_escape:
+Escaping special characters in XML:
 <%xml_escape\x < a & b%>
--- a/src/Manifest.mf	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/Manifest.mf	Tue Feb 19 20:32:17 2008 +0300
@@ -1,2 +1,3 @@
 Manifest-Version: 1.0
 Main-Class: kryshen.tema.Tema
+Class-Path: lib/commons-cli-1.1.jar
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/Context.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Context.java,v 1.11 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tema template context.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Context {
+    private Map<String, Object> definitions = new HashMap<String, Object>();
+    private Context superContext;
+    private File baseDir;
+    
+    public Context() {
+        this(null, null);
+    }
+    
+    public Context(Context superContext) {
+        this(superContext, null);
+    }
+    
+    public Context(File baseDir) {
+        this(null, baseDir);
+    }
+    
+    public Context(Context superContext, File baseDir) {
+        this.superContext = superContext;
+        this.baseDir = baseDir;
+        
+        definitions.put("this", this);
+        
+        if (superContext != null)
+            definitions.put("super", superContext);
+    }
+    
+    public void clear() {
+        definitions.clear();
+    }
+    
+    public boolean export(String name) {
+        Object value = definitions.get(name);
+        
+        if (value == null)
+            return false;
+        
+        export(name, value);
+        return true;
+    }
+    
+    public void export(String name, Object value) {
+        if (superContext == null) {
+            set(name, value);
+            return;
+        }
+        
+        definitions.remove(name);
+        superContext.export(name, value);
+    }
+    
+    public Object get(String name) throws TemplateException {
+        Object value = definitions.get(name);
+        
+        if (value == null && superContext != null)
+            return superContext.get(name);
+        
+        return value;
+    }
+    
+    /**
+     * Set the definition value.
+     *
+     * @param name Variable name.
+     * @param value New variable value.
+     */
+    public void set(String name, Object value) {
+        definitions.put(name, value);
+    }
+    
+    /**
+     * Remove the definition recursively.
+     * 
+     * @param name Definition name.
+     * @return true if the definition was found and removed.
+     */
+    public boolean unset(String name) {
+        if (definitions.remove(name) != null)
+            return true;
+        
+        if (superContext != null)
+            return superContext.unset(name);
+        
+        return false;
+    }
+    
+    /**
+     * Returns base directory which should be used to resolve
+     * relative path names in template.
+     */
+    public File getBaseDirectory() {
+        if (baseDir == null && superContext != null)
+            return superContext.getBaseDirectory();
+        
+        return baseDir;
+    }
+}
--- a/src/kryshen/tema/Function.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/Function.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,33 +1,34 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Function.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: Function.java,v 1.13 2008/02/19 16:21:00 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
-import java.sql.ResultSet;
+import java.io.IOException;
+import java.io.Writer;
 
 /**
- * Abstact class for TEMA functions.
+ * Abstact class for Tema functions.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public abstract class Function {
 
--- a/src/kryshen/tema/FunctionDataParser.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/FunctionDataParser.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,140 +1,204 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: FunctionDataParser.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: FunctionDataParser.java,v 1.27 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
-
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
 import static kryshen.tema.TemplateParser.Result;
-import static kryshen.tema.TemplateParser.FunctionData;
+import static kryshen.tema.TemplateParser.DataFormat;
+import kryshen.tema.TemplateParser.DataFormat;
 import static kryshen.tema.TemplateParser.Terminator;
+import kryshen.tema.io.CopyWriter;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * Parser for a function data.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class FunctionDataParser {
     static final char[] ARG_SEPARATORS = TemplateParser.LIST_SEPARATORS;
-
+    
     private TemplateParser tp;
-    private FunctionData fd;
+    private DataFormat fd;
     private TemplateReader in;
-
+    
     private boolean available = true;
-    private boolean first = true;
-
-    FunctionDataParser(TemplateParser tp, FunctionData fd,
-                       TemplateReader in) {
+    
+    private int lastReturnCode = 0;
+    
+    FunctionDataParser(TemplateParser tp, DataFormat fd, TemplateReader in)
+    throws IOException, TemplateException {
         this.tp = tp;
         this.fd = fd;
         this.in = in;
+        
+        // Check for empty function data.
+        checkAvailable();
     }
-
-    private TemplateParser.Result parseData(Writer out, boolean argument)
-	throws IOException, TemplateException {
+    
+    private void checkAvailable() throws IOException, TemplateException {
+        if (tp.checkCloseBracket(in)) {
+            // Complete parsing the instruction.
+            skipData();
+        }
+    }
+    
+    private int parseData(Writer out, boolean argument, boolean skip)
+    throws IOException, TemplateException {
         
         if (!available)
             throw new TemplateException
-		("Unexpected end of instruction data.", in);
-
+                    ("Unexpected end of instruction data.", in);
+        
+        DataFormat fd = skip ? this.fd.noCall() : this.fd;
+        
         TemplateParser.Result r;
-
-        if (fd == FunctionData.RECURSIVE) {
-            r = tp.parse(in, out, true, 
-                         argument ? ARG_SEPARATORS : null,
-                         (argument || !first) ? ARG_SEPARATORS : null);
-        } else if (fd == FunctionData.NONRECURSIVE) {
-            r = tp.parse(in, out, false, 
-                         argument ? ARG_SEPARATORS : null,
-                         (argument || !first) ? ARG_SEPARATORS : null);
-        } else if (fd == FunctionData.SUBFUNCTION && argument) {
-            // Subfunction can pass a list of arguments
-            StringWriter sw = new StringWriter();
-            tp.parseFunction(in, sw);
-            sw.close();
-            in = new TemplateReader(new StringReader(sw.toString()), in);
-            fd = FunctionData.NONRECURSIVE;
-            r = parseData(out, true);
-        } else if (fd == FunctionData.SUBFUNCTION) {
-            r = new Result(null, tp.parseFunction(in, out), false);
+        
+//        if (fd.subfunction && argument) {
+//            // Allow subfunction to pass a list of arguments
+//            StringWriter sw = new StringWriter();
+//            tp.parse(in, sw, fd);
+//            sw.close();
+//            in = new TemplateReader(new StringReader(sw.toString()), in);
+//            this.fd = DataFormat.VERBATIM;
+//            r = parseData(out, true, false);
+//        } else
+        if (fd.subfunction) {
+            r = tp.parse(in, out, fd);
+        } else if (argument) {
+            // Skip duplicate separators before argument.
+            tp.skip(in, ARG_SEPARATORS);
+            
+            r = tp.parse(in, out, fd, ARG_SEPARATORS);
+            
+            // Skip duplicate separators after argument.
+            if (r.terminator == Terminator.SEPARATOR) {
+                tp.skip(in, ARG_SEPARATORS);
+                checkAvailable();
+            }
         } else {
-            throw new IllegalArgumentException("Invalid FunctiodData: " + fd);
+            r = tp.parse(in, out, fd, null);
         }
-
-        if (r.terminator != TemplateParser.Terminator.SEPARATOR)
+        
+        if (r.terminator != Terminator.SEPARATOR)
             available = false; // No more function data available.
-
-        first = false;
-        return r;
+        
+        lastReturnCode = r.retCode;
+        return r.retCode;
+    }
+    
+    /**
+     * Parse function data.
+     */
+    public int parseData(Writer out) throws IOException, TemplateException {
+        return parseData(out, false, false);
     }
-
-    public int parseData(Writer out) throws IOException, TemplateException {
-        return parseData(out, false).retCode; 
+    
+    /**
+     * Skip function data (do not call any functions).
+     */
+    public void skipData() throws IOException, TemplateException {
+        parseData(NullWriter.INSTANCE, false, true);
     }
-
-    public String getData() throws IOException, TemplateException {        
+    
+    /**
+     * Get function data as string.
+     */
+    public String getData() throws IOException, TemplateException {
         StringWriter sw = new StringWriter();
         parseData(sw);
         sw.close();
         return sw.toString();
     }
-
+    
+    /**
+     * Parse function data into specified <code>Writer</code>.
+     * Returns copy of the parsed data as string.
+     */
+    public String getData(Writer out) throws IOException, TemplateException {
+        StringWriter sw = new StringWriter();
+        parseData(new CopyWriter(sw, out));
+        sw.close();
+        return sw.toString();
+    }
+    
     public int parseNextArg(Writer out) throws IOException, TemplateException {
-        TemplateParser.Result r = parseData(out, true);
-
-        // Ignore empty arguments.
-        while (r.empty && available) {
-            r = parseData(out, true);
-        }
-
-        return r.retCode;
+        return parseData(out, true, false);
     }
-
-    public String getNextArg() throws IOException, TemplateException {        
+    
+    public void skipNextArg() throws IOException, TemplateException {
+        parseData(NullWriter.INSTANCE, true, true);
+    }
+    
+    public String getNextArg() throws IOException, TemplateException {
         StringWriter sw = new StringWriter();
         parseNextArg(sw);
         sw.close();
         return sw.toString();
     }
-
+    
     public List<String> getArgs() throws IOException, TemplateException {
-	List<String> l = new ArrayList<String>();
-
-	while (available) {
-	    l.add(getNextArg());
-	}
-	
-	return l;
+        List<String> l = new ArrayList<String>();
+        
+        while (available) {
+            l.add(getNextArg());
+        }
+        
+        return l;
     }
-
+    
     public boolean hasMoreData() {
         return available;
     }
-
+    
+    public int getLastReturnCode() {
+        return lastReturnCode;
+    }
+    
     public TemplateParser getTemplateParser() {
         return tp;
     }
     
+    public Context getContext() {
+        return tp.getContext();
+    }
+    
     public TemplateReader getTemplateReader() {
         return in;
     }
+    
+    public File createFile(String path) {
+        File file = new File(path);
+        
+        if (file.isAbsolute())
+            return file;
+        
+        return new File(tp.getContext().getBaseDirectory(), path);
+    }
 }
--- a/src/kryshen/tema/Functions.java	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
- *
- *  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
- *
- *  $Id: Functions.java,v 1.11 2006/12/14 19:44:31 mikhail Exp $
- */
-
-package kryshen.tema;
-
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.functions.*;
-
-/**
- * Standard TEMA functions.
- *
- * @author Mikhail A. Kryshen
- */
-public class Functions {
-    // TODO: arithmetics, conditions
-
-    /**
-     * Register all standard functions.
-     *
-     * @param p TemplateParser to register functions in.
-     */
-    static void registerAllFunctions(TemplateParser p) {
-        p.registerFunction("!",          Standard.NULL_OUTPUT);
-        p.registerFunction("set",        Standard.SET);
-        p.registerFunction("load",       Standard.LOAD);
-        p.registerFunction("query",      Standard.QUERY);
-        p.registerFunction("prepare",    Standard.PREPARE);
-        p.registerFunction("replace",    Standard.REPLACE);
-        p.registerFunction("xml_escape", Standard.XML_ESCAPE);
-        p.registerFunction("xml_cdata",  Standard.XML_CDATA);
-
-        p.registerFunction("define",     Define.DEFINE);
-
-        p.registerFunction("write",      IO.WRITE);
-        p.registerFunction("read",       IO.READ);
-        p.registerFunction("include",    IO.INCLUDE);
-        p.registerFunction("copy",       IO.COPY);
-
-	p.registerFunction("false",      Logics.FALSE);
-	p.registerFunction("true",       Logics.TRUE);
-        p.registerFunction("optional",   Logics.OPTIONAL);
-
-        p.registerFunction("image",      ImageConverter.IMAGE);
-
-	/* 
-	   Other functuons:
-	   db - defined in Tema.query().
-	   nextarg, data - defined in Define.invoke().
-	*/
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/GlobalContext.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: GlobalContext.java,v 1.16 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema;
+
+import java.io.File;
+import kryshen.tema.functions.Database;
+import kryshen.tema.functions.Define;
+import kryshen.tema.functions.IO;
+import kryshen.tema.functions.ImageConverter;
+import kryshen.tema.functions.Control;
+import kryshen.tema.functions.Standard;
+import kryshen.tema.functions.Strings;
+
+/**
+ * Context to hold exported variables and standard Tema definitions.
+ *
+ * @author Mikhail Kryshen
+ */
+class GlobalContext extends Context {    
+    
+    GlobalContext() {
+        super(new File(System.getProperty("user.dir")));
+        
+        // Register all standard functions.
+        // TODO: arithmetics
+        
+        set("tema",       Standard.TEMA);
+        set("echo",       Standard.ECHO);
+        set("",           Standard.ECHO);
+        set("!",          Standard.SKIP);
+        set("silent",     Standard.SILENT);
+        set("set",        Standard.SET);        
+        set("unset",      Standard.UNSET);
+        set("export",     Standard.EXPORT);
+        set("assign",     Standard.ASSIGN);
+        set("invoke",     Standard.INVOKE);
+        set("load",       Standard.LOAD);
+        
+        set("to_upper",   Strings.TO_UPPER);
+        set("to_lower",   Strings.TO_LOWER);
+        set("substring",  Strings.SUBSTRING);
+        set("equal",      Strings.EQUAL);
+        set("xml_escape", Strings.XML_ESCAPE);
+        set("xml_cdata",  Strings.XML_CDATA);
+        set("char",       Strings.CHAR);
+        set("replace",    Strings.REPLACE);
+        set("regex_match",         Strings.REGEX_MATCH);
+        set("regex_replace_first", Strings.REGEX_REPLACE_FIRST);
+        set("regex_replace_all",   Strings.REGEX_REPLACE_ALL);
+        
+        set("define",     Define.DEFINE);
+        
+        set("write",      IO.WRITE);
+        set("read",       IO.READ);
+        set("include",    IO.INCLUDE);
+        set("copy",       IO.COPY);
+        set("file",       IO.FILE);
+        
+        set("false",      Control.FALSE);
+        set("true",       Control.TRUE);
+        set("not",        Control.NOT);
+        set("optional",   Control.OPTIONAL);
+        set("if",         Control.IF);
+        set("if_else",    Control.IF_ELSE);
+        set("while",      Control.WHILE);
+        
+        set("db_connect",    Database.CONNECT);
+        set("db_prepare",    Database.PREPARE);
+        set("db_query",      Database.QUERY);
+        
+        set("convert_image", ImageConverter.CONVERT_IMAGE);
+        
+        // Other functions are defined in classes Database and Define.
+    }
+}
--- a/src/kryshen/tema/Tema.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/Tema.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,364 +1,248 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Tema.java,v 1.17 2006/12/14 19:44:31 mikhail Exp $
+ *  $Id: Tema.java,v 1.42 2008/02/19 17:14:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.Connection;
-import java.sql.Statement;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
 import java.sql.SQLException;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Properties;
-import java.util.List;
-import java.util.Collections;
-
-import java.io.*;
-
 import kryshen.tema.demo.DemoFrame;
+import kryshen.tema.io.TemplateReader;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
 
 /**
  * Tema main class.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Tema {
-    public static final String TITLE = "TEMA";
-    public static final String VERSION = "0.1";
-    public static final String AUTHOR = "Mikhail A. Kryshen";
-
-    private static final String CONFIG_FILE = "tema.properties";
-    private static final String ENV_PREFIX = "kryshen.tema.";
-
-    private static Properties config = new Properties();
-    private static Connection connection = null;
-
-    private static String inputEncoding;
-    private static String outputEncoding;
-
-    private static Map<File, String> fileCache = null;
-
-    public static void main(String[] args) 
-	throws IOException, SQLException,
-	       ClassNotFoundException, InstantiationException, 
-	       IllegalAccessException {
-	
+    public static final String TITLE = "Tema";
+    public static final String VERSION = "0.3";
+    public static final String AUTHOR = "Mikhail Kryshen";    
+    public static final String COPYRIGHT = 
+            "Copyright 2008 Mikhail Kryshen <mikhail@kryshen.pp.ru>";
+    public static final String LICENSE = 
+            "This is free software. " + 
+            "You may redistribute copies of it under the terms of" + 
+            System.getProperty("line.separator") + 
+            "the GNU Lesser General Public License version 3 or later" + 
+            System.getProperty("line.separator") + 
+            "<http://gnu.org/licenses/lgpl.html>.";
+        
+    private static final String DEFAULT_CHARSET =
+            Charset.defaultCharset().name();
+    
+    private static String inputEncoding = DEFAULT_CHARSET;
+    private static String outputEncoding = DEFAULT_CHARSET;
+    
+    public static void main(String[] args)
+    throws IOException, SQLException,
+            ClassNotFoundException, InstantiationException,
+            IllegalAccessException {
+        
         boolean demo = false;
         boolean version = false;
         boolean help = false;
-
-        // Parse arguments.
-        for (int i = 0; i < args.length; i++) {
-            if ("-demo".equals(args[i]) || 
-                "-d".equals(args[i])) {
-                demo = true;
-            } else if ("-version".equals(args[i]) ||
-                       "-v".equals(args[i])) {
-                version = true;
-            } else if ("-help".equals(args[i]) ||
-                       "-h".equals(args[i]) ||
-                       "-usage".equals(args[i]) ||
-                       "-u".equals(args[i])) {
-                help = true;
-            } else {
-                System.err.println("Invalid option: " + args[i]);
-                return;
-            }
+        
+        Options options = new Options();
+        
+        options.addOption(null, "demo", false, "demo mode");
+        options.addOption("v", "version", false,
+                "print the version information and exit");
+        options.addOption("h", "help", false,
+                "print this help message");
+        options.addOption("o", "output", true,
+                "output file");
+        options.addOption(null, "log", true,
+                "log all error messages to file");
+        options.addOption(null, "input-encoding", true,
+                "input encoding");
+        options.addOption(null, "output-encoding", true,
+                "output encoding");
+        
+        CommandLineParser parser = new GnuParser();
+        CommandLine line;
+        
+        try {
+            line = parser.parse(options, args );
+        } catch (ParseException ex) {
+            System.err.println(ex.getMessage());
+            System.exit(1);
+            return; // to avoid "not initialized" compile-time errors.
         }
-
-        if (version || help) {
-            System.err.println(TITLE + " " + VERSION + " by " + AUTHOR);        
-
-            if (help) {
-                PrintStream err = System.err;
-                
-                err.println();
-                err.println("Usage: tema [OPTIONS]");
-                err.println();
-                err.println("Options:");
-                err.println("  -d[emo]             Demo mode");
-                err.println("  -v[ersion]          Print version");
-                err.println("  -h[help] -u[sage]   Print this help message");
-            }
+        
+        if (line.hasOption("version")) {
+            System.err.println(TITLE + " " + VERSION);
+            System.err.println(COPYRIGHT);
+            System.err.println(LICENSE);
             return;
         }
-
-        if (demo) {
-            // Run in demo mode.
+        
+        if (line.hasOption("help")) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp("tema [options] [files]", options);
+            return;
+        }       
+        
+        if (line.hasOption("input-encoding")) {
+            inputEncoding = line.getOptionValue("input-encoding");
+        }
+        
+        if (line.hasOption("output-encoding")) {
+            outputEncoding = line.getOptionValue("output-encoding");
+        }
+        
+        if (line.hasOption("log")) {
+            System.setErr(new PrintStream
+                    (new FileOutputStream(line.getOptionValue("log"))));
+        }
+        
+        if (line.hasOption("demo")) {
+            // Open the demo console.
             DemoFrame df = new DemoFrame();
             df.pack();
             df.setVisible(true);
             return;
         }
-
-        process();
-    }
-
-    /**
-     * Standard execution.
-     */
-    private static void process()
-        throws IOException, SQLException,
-               ClassNotFoundException, InstantiationException, 
-               IllegalAccessException {
-
-        InputStream configIn = new FileInputStream(CONFIG_FILE);
-        config.load(configIn);
-        configIn.close();
+        
+        Writer out;
         
-        configure();
-	
-	String main = getProperty("main_template");
-	String output = getProperty("output");
-	
-	Writer out;
-
-	if ("stderr".equals(output))
-	    out = new OutputStreamWriter(System.err);
-	else
-	    out = new OutputStreamWriter(System.out);
-
-        TemplateReader in = createTemplateReader(new File(main));
-        TemplateParser p = new TemplateParser();
-        TemplateException te = null;
-
-	try {
-	    p.parse(in, out);
-        } catch (TemplateException e) {
-            te = e;
-	} finally {
-	    in.close();
-	    out.flush();
-	}  
-
-        if (te != null) {
-            System.err.println(te.getMessage());
-            if (te.getCause() != null) {
-                System.err.println("Caused by " + te.getCause());
-            }
+        if (line.hasOption("output")) {
+            File outfile = new File(line.getOptionValue("output"));
+            out = createFileWriter(outfile);
         } else {
-            System.err.println("Done");
+            out = new OutputStreamWriter(System.out, outputEncoding) {
+                @Override
+                public void write(int c) throws IOException {
+                    super.write(c);
+                    if (c == '\r' || c == '\n')
+                        flush();
+                }
+                
+                @Override
+                public void write(char cbuf[], int off, int len)
+                throws IOException {
+                    super.write(cbuf, off, len);
+                    flush();
+                }
+                
+                @Override
+                public void write(String str, int off, int len)
+                throws IOException {
+                    super.write(str, off, len);
+                    flush();
+                }
+            };
         }
-    }
-
-    private static void configure() 
-        throws IOException, SQLException, IllegalAccessException,
-               ClassNotFoundException, InstantiationException {
-
-        String logFile = getProperty("log");
-        if (logFile != null && logFile.length() > 0)
-            System.setErr(new PrintStream(new FileOutputStream(logFile)));
-
-	if (Boolean.parseBoolean(getProperty("cache_read")) == true)
-	    fileCache = new HashMap<File, String>();
-
-	inputEncoding = getProperty("input_encoding", "iso-8859-1");
-	outputEncoding = getProperty("output_encoding", "iso-8859-1");
-
-	String resourceProperty = getProperty("resource");
-
-	if (resourceProperty != null) {
-	    String driverProperty = getProperty("driver");
-	    if (driverProperty != null) {
-		// Register driver.  
-		Driver d = (Driver)Class.forName(driverProperty).newInstance();
-	    }
-	
-	    // Open connection.
-	    connection = DriverManager.getConnection(resourceProperty);
-	}
-    }
-
-    
-
-    /**
-     * Get configuration property.
-     */
-    public static String getProperty(String name) {
-        String value = System.getProperty(ENV_PREFIX + name);
-        if (value != null) return value;
-        return config.getProperty(name);
-    }
-
-    /**
-     * Get configuration property.
-     */
-    public static String getProperty(String name, String fallback) {
-	String value = getProperty(name);
-	if (value != null) return value;
-	return fallback;
+        
+        String[] files = line.getArgs();
+        
+        if (files.length == 0) {
+            TemplateReader in = createTemplateReader(System.in);
+            if (!process(in, out)) {
+                System.exit(1);
+            }
+        }
+        
+        for (String file: files) {
+            TemplateReader in = createTemplateReader(new File(file));
+            if (!process(in, out)) {
+                System.exit(1);
+            }
+        }
+        
+        out.close();
     }
-
-    /**
-     * Process database query.
-     *
-     * @param templateReader reader for the template 
-     *                       to fill with data.
-     * @param ps query statement to execute.
-     * @param args list of query parameters.
-     * @param superParser invoking object.
-     * @param out Writer to output processed data.
-     *
-     * @return number of processed rows in query result.
-     */
-    public static int query(TemplateReader templateReader,
-                            PreparedStatement ps,
-                            List<String> args,
-                            TemplateParser superParser, 
-                            Writer out)
-	throws IOException, SQLException, TemplateException {
-
-	int i = 1;
-
-	for (String arg : args) {
-	    ps.setString(i++, arg);
-	}
-	
-	ResultSet r = ps.executeQuery();
-	ResultSetMetaData rm = r.getMetaData();
-	int columnCount = rm.getColumnCount();
-
-	String[] names = new String[columnCount];
-	
-	for (int j = 0; j < columnCount; j++) {
-	    names[j] = rm.getColumnName(j + 1);
-	}
-
-	final TemplateParser p = new TemplateParser(superParser);
-	final HashMap<String, Object> fields = new HashMap<String, Object>();
-
-        p.registerFunction("db", new Function() {
-                public int invoke(FunctionDataParser fdp, 
-                                  final Writer out)
-                    throws IOException, TemplateException {
-                    
-                    String name = fdp.getData();
-                    Object value = fields.get(name);
-		    
-                    return p.parseValue(value, out);
-                }
-            });
-
-	for (i = 1; r.next(); i++) {
-
-	    for (int j = 0; j < columnCount; j++) {
-		Object value = r.getObject(j + 1);
-		if (r.wasNull()) value = null;
-                
-		fields.put(names[j], value);
-	    }
-
-	    p.setValue("number", i);
-            p.parse(templateReader, out);
-	    templateReader.reset();
-            fields.clear();
-	}
-
-	r.close();
-	ps.clearParameters();
-	return i - 1;
+    
+    private static boolean process(TemplateReader in, Writer out)
+    throws IOException {
+        
+        TemplateParser p = new TemplateParser();
+        TemplateException ex = null;
+                        
+        p.getContext().set("input_encoding",  inputEncoding);
+        p.getContext().set("output_encoding", outputEncoding);
+        
+        try {
+            p.parse(in, out);
+        } catch (TemplateException e) {
+            ex = e;
+        } finally {
+            in.close();
+        }
+        
+        if (ex != null) {
+            System.err.println(ex.getMessage());
+            if (ex.getCause() != null) {
+                System.err.println("Caused by " + ex.getCause());
+            }
+            return false;
+        }
+        
+        return true;
     }
-
-    public static BufferedWriter createFileWriter(String filename) 
-        throws IOException {
-
-	OutputStream os = new FileOutputStream(filename);
-	return new BufferedWriter
-            (new OutputStreamWriter(os, Tema.outputEncoding));
-    }
-
-    public static BufferedReader createFileReader(File file)
-        throws IOException {
-
-	InputStream is = new FileInputStream(file);
-	return new BufferedReader
-            (new InputStreamReader(is, Tema.inputEncoding));
-    }
-
-    public static TemplateReader createTemplateReader(File file)
-        throws IOException {
-
-        return new TemplateReader
-            (new LineNumberReader(createCachedFileReader(file)), file.getPath());
-    }
-
-    /**
-     * Creates StringReader for a cached file if caching is enabled or
-     * a BufferedReader.
-     *
-     * @param file File to read.
-     * @return Reader for a file.
-     */
-    public static Reader createCachedFileReader(File file) 
-        throws IOException {
+    
+    public static BufferedWriter createFileWriter(File file)
+    throws IOException {
         
-	if (fileCache != null) return new StringReader(readFile(file));
-	else return createFileReader(file);
+        OutputStream os = new FileOutputStream(file);
+        return new BufferedWriter
+                (new OutputStreamWriter(os, Tema.outputEncoding));
     }
-
-    /**
-     * Read text file.
-     *
-     * @return file contents.
-     */
-    public static String readFile(File file) throws IOException {
-	String data;
-        
-	if (fileCache != null) {
-	    data = fileCache.get(file);	    
-	    if (data != null) return data;
-	}
+    
+    public static BufferedReader createFileReader(File file)
+    throws IOException {
         
-        Reader r = new BufferedReader(createFileReader(file));
-
-        // FIXME: possible overflow (long -> int).
-	StringBuffer sb = new StringBuffer((int)file.length());
-
-	try {
- 	    for (int c = r.read(); c >= 0; c = r.read()) {
- 		sb.append((char)c);
- 	    }
-	} finally {
-	    r.close();
-	}
-
-	data = sb.toString();
-
-	if (fileCache != null) {
-	    fileCache.put(file, data);
-	}
-
-	return data;
+        InputStream is = new FileInputStream(file);
+        return new BufferedReader
+                (new InputStreamReader(is, Tema.inputEncoding));
     }
-
-    /**
-     * Get database connection.
-     *
-     * @return database connection if any was configured.
-     */
-    public static Connection getDbConnection() {
-        return connection;
+    
+    public static TemplateReader createTemplateReader(File file)
+    throws IOException {
+        
+        return new TemplateReader(createFileReader(file), file.getPath());
+    }
+    
+    public static TemplateReader createTemplateReader(InputStream in)
+    throws IOException {
+        
+        return new TemplateReader(
+                new InputStreamReader(in, Tema.inputEncoding));
     }
 }
--- a/src/kryshen/tema/TemplateException.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/TemplateException.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,29 +1,33 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: TemplateException.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: TemplateException.java,v 1.12 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
+import kryshen.tema.io.TemplateReader;
+
 /**
  * Signals that template parsing fails.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class TemplateException extends Exception {
     //private String source;
--- a/src/kryshen/tema/TemplateParser.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/TemplateParser.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,296 +1,445 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: TemplateParser.java,v 1.11 2006/12/14 19:44:31 mikhail Exp $
+ *  $Id: TemplateParser.java,v 1.58 2008/02/19 00:20:48 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.Writer;
+import kryshen.tema.io.TemplateReader;
 
 /**
- * Parser for TEMA templates.
+ * Parser for Tema templates.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class TemplateParser {
-    // TODO: report non-critical errors and warnings.
-
-    static final String SUPER_PREFIX = "super.";
-
     /* Brackets. */
-    static final char[] BR_LEFT = {'<', '['};
-    static final char[] BR_RIGHT = {'>', ']'};
-
-    static final char BR_IN = '%';
+    public static final String[] BRACKET_OPEN = {"<%", "[%"};
+    public static final String[] BRACKET_CLOSE = {"%>", "%]"};
+    
+    public static final String ESCAPE_NEWLINE = "\\";
+    public static final String ESCAPE_WHITESPACE = "\\\\";
     
     /* Separators. */
-    static final char[] REC_DATA_SEPARATORS = {':'};
-    static final char[] NONREC_DATA_SEPARATORS = {'\\', '`'}; 
-    static final char[] LIST_SEPARATORS = {' ', '\t', '\r', '\n'};
-
+    public static final char[] REC_DATA_SEPARATORS = {':'};
+    public static final char[] VERBATIM_DATA_SEPARATORS = {'\\', '`'};
+    public static final char[] NOCALL_DATA_SEPARATORS = {'#'};
+    public static final char[] LIST_SEPARATORS = {' ', '\t', '\r', '\n'};
+    
     /**
-     * Methods of parsing function arguments and data.
+     * Specifies how to parse function arguments and data.
      */
-    static enum FunctionData {
-	SUBFUNCTION, RECURSIVE, NONRECURSIVE;
+    static enum DataFormat {
+        RECURSIVE(true, false),
+        VERBATIM(false, false),
+        NOCALL(false, false),
+        SUBFUNCTION(true, true),
+        SUBFUNCTION_NOCALL(false, true);
+        
+        public final boolean call;
+        public final boolean subfunction;
+        
+        DataFormat(boolean call, boolean subfunction) {
+            this.call = call;
+            this.subfunction = subfunction;
+        }
+        
+        /**
+         * Returns no-call equivalent to this format.
+         */
+        public DataFormat noCall() {
+            if (this == RECURSIVE)
+                return NOCALL;
+            
+            if (this == SUBFUNCTION)
+                return SUBFUNCTION_NOCALL;
+            
+            return this;
+        }
     };
-
+    
+    /**
+     * Specifies how the template instruction was terminated.
+     */
     static enum Terminator {
-	EOF, BRACKET, SEPARATOR;
+        EOF, BRACKET, SEPARATOR;
     };
-
+    
+    /**
+     * The result of evaluating template instruction.
+     */
     static class Result {
-	Terminator terminator;
-	int retCode;
-
+        Terminator terminator;
+        int retCode;
+        
         /** No text had been parsed. */
-        boolean empty; 
-
-	Result(Terminator terminator, int retCode, boolean empty) {
+        boolean empty;
+        
+        Result(Terminator terminator, int retCode, boolean empty) {
             this.terminator = terminator;
             this.retCode = retCode;
             this.empty = empty;
         }
-
+        
         Result() {
             this(null, -1, true);
-        }       
-    };    
-
-    private Map<String, Object> variables = new HashMap<String, Object>();
-    private TemplateParser superParser;
-
+        }
+    };
+    
+    protected final Context context;
+    
     private int termBracket = -1;
-
+    
     public TemplateParser() {
-        this(null);
+        this.context = new Context(new GlobalContext());
     }
-
+    
+    public TemplateParser(File baseDir) {
+        this.context = new Context(new GlobalContext(), baseDir);
+    }
+    
     public TemplateParser(TemplateParser superParser) {
-	this.superParser = superParser;
-
-        Functions.registerAllFunctions(this);
+        this.context = new Context(superParser.context);
     }
-
-    public void registerFunction(String name, Function f) {
-        variables.put(name, f);
+    
+    public TemplateParser(TemplateParser superParser, File baseDir) {
+        this.context = new Context(superParser.context, baseDir);
+    }
+    
+    public TemplateParser(Context context) {
+        this.context = context;
     }
-
-    public int parse(TemplateReader in, Writer out) 
-	throws IOException, TemplateException {
-	
-	return parse(in, out, true, null, null).retCode;
+    
+    /**
+     * Parse template
+     *
+     * @param in TemplateReader to read template data.
+     * @param out Writer for the processed data.
+     * @throws IOException if case of I/O error.
+     * @throws TemplateException in case of parsing error (syntax
+     * error or invalid arguments in template instructions).
+     */
+    public int parse(TemplateReader in, Writer out)
+    throws IOException, TemplateException {
+        return parse(in, out, DataFormat.RECURSIVE, null).retCode;
     }
-
+    
+    Result parse(TemplateReader in, Writer out, DataFormat format)
+    throws IOException, TemplateException {
+        return parse(in, out, format, null);
+    }
+    
     /**
-     *  Parse template.
+     * Parse template.
      *
      * @param in TemplateReader to read template data.
      * @param out Writer to write processed data.
-     * @param recursive enables recursive processing.
-     * @param separators characters which terminate parsing block.
-     * @param leading characters to ignore at the beginning of the block.
+     * @param DataFormat specifies parsing mode.
      */
-    Result parse(TemplateReader in, Writer out, boolean recursive, 
-                 char[] separators, char[] leading)
-	throws IOException, TemplateException {
-
-	Result result = new Result();
-	int lc = -1;
-
-	while (true) {
-	    int c = in.read();
+    Result parse(TemplateReader in, Writer out, DataFormat format,
+            char[] separators)
+            throws IOException, TemplateException {
+        
+        Result result = new Result();
+        
+        if (format == DataFormat.SUBFUNCTION ||
+                format == DataFormat.SUBFUNCTION_NOCALL) {
             
-            if (leading != null && isSeparator(c, leading))
-                continue;
-            else
-                leading = null;
-
-            boolean leftBracket = false;
-            int index;
-
-            for (index = 0; index < BR_LEFT.length; index++) {
-                if (lc == BR_LEFT[index]) {
-                    leftBracket = true;
-                    break;
+            result.retCode = parseFunction(in, out, format);
+            result.empty = false;
+            return result;
+        }
+        
+        while (true) {
+            if (format != DataFormat.VERBATIM) {
+                int openBracket = matchInput(in, BRACKET_OPEN);
+                
+                if (openBracket >= 0) {
+                    if (result.retCode < 0)
+                        result.retCode = 0;
+                    
+                    if (!format.call) {
+                        out.write(BRACKET_OPEN[openBracket]);
+                    }
+                    
+                    int tb = termBracket;
+                    termBracket = openBracket;
+                    result.retCode += Math.abs(parseFunction(in, out, format));
+                    result.empty = false;
+                    termBracket = tb;
+                    
+                    if (!format.call) {
+                        out.write(BRACKET_CLOSE[openBracket]);
+                    }
+                    
+                    continue;
                 }
             }
-
-	    if (recursive && leftBracket && c == BR_IN) {
-		
-                if (result.retCode < 0)
-                    result.retCode = 0;
-
-		lc = -1;
-                int tb = termBracket;
-                termBracket = BR_RIGHT[index];
-		result.retCode += parseFunction(in, out);
-                termBracket = tb;
-		
-	    } else if (lc == BR_IN && c == termBracket) {
-		
-		lc = -1;
-		result.terminator = Terminator.BRACKET;
-		break;
-		
-	    } else {
-
-		if (lc >= 0)
-		    out.write(lc);
-	    
-		lc = c;
-	    }
-
-	    if (c < 0) {
-		result.terminator = Terminator.EOF;
-		break; 
-	    } else if (separators != null && isSeparator(c, separators)) {
+            
+            if (matchCloseBracket(in)) {
+                result.terminator = Terminator.BRACKET;
+                skipEscaped(in);
+                break;
+            }
+            
+            int c = in.read();
+            
+            if (c < 0) {
+//                if (termBracket >= 0) {
+//                    throw new TemplateException("Unexpected end of file", in);
+//                }
+                
+                result.terminator = Terminator.EOF;
+                break;
+            }
+            
+            if (separators != null && isSeparator(c, separators)) {
                 result.terminator = Terminator.SEPARATOR;
                 break;
             }
-
+            
+            out.write(c);
             result.empty = false;
-	}
-
-	return result;
+        }
+        
+        return result;
+    }
+    
+    /**
+     * Check if the closing bracket sequence follows in the reader.
+     */
+    boolean checkCloseBracket(TemplateReader in) throws IOException {
+        boolean match = matchCloseBracket(in);
+        
+        if (!match)
+            return false;
+        
+        // Put the matched characters back to the stream
+        in.unread(BRACKET_CLOSE[termBracket].toCharArray());
+        
+        return true;
+    }
+    
+    private boolean matchCloseBracket(TemplateReader in)
+    throws IOException {
+        
+        if (termBracket < 0)
+            return false;
+        
+        return matchInput(in, BRACKET_CLOSE[termBracket]);
+    }
+    
+    private int matchInput(TemplateReader in, String[] s)
+    throws IOException {
+        for (int i = 0; i < s.length; i++) {
+            if (matchInput(in, s[i]))
+                return i;
+        }
+        
+        return -1;
     }
-
+    
+    private boolean matchInput(TemplateReader in, String s)
+    throws IOException {
+        int n = s.length();
+        for (int k = 0; k < n; k++) {
+            int c = in.read();
+            
+            if (c != s.charAt(k)) {
+                if (c >= 0) {
+                    in.unread(c);
+                }
+                
+                for (int j = k - 1; j >= 0; j--) {
+                    in.unread(s.charAt(j));
+                }
+                
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    void skip(TemplateReader in, char[] chars) throws IOException {
+        readLoop:
+            while (true) {
+                int c = in.read();
+                
+                if (c < 0)
+                    break;
+                
+                for (int i = 0; i < chars.length; i++) {
+                    if (c == chars[i])
+                        continue readLoop;
+                }
+                
+                in.unread(c);
+                break;
+            }
+    }
+    
+    private void skipEscaped(TemplateReader in) throws IOException {
+        if (matchInput(in, ESCAPE_WHITESPACE)) {
+            int c = in.read();
+            
+            if (c < 0)
+                return;
+            
+            if (!Character.isWhitespace(c)) {
+                in.unread(c);
+                in.unread(ESCAPE_WHITESPACE.toCharArray());
+                return;
+            }
+            
+            do {
+                c = in.read();
+            } while (Character.isWhitespace(c));
+            
+            if (c >= 0)
+                in.unread(c);
+            
+            return;
+        }
+        
+        matchInput(in,
+                new String[] {
+            ESCAPE_NEWLINE + "\r\n",
+            ESCAPE_NEWLINE + "\r",
+            ESCAPE_NEWLINE + "\n"});
+    }
+    
     boolean isSeparator(int c, char[] separators) {
         for (char p : separators) {
             if (c == p) return true;
         }
-
+        
         return false;
     }
-
-    int parseFunction(TemplateReader in, Writer out)
-	throws IOException, TemplateException {
+    
+    private int parseFunction(TemplateReader in, Writer out, DataFormat fd)
+    throws IOException, TemplateException {
+        
+        StringBuffer sb = new StringBuffer();
         
-	StringBuffer sb = new StringBuffer();
-        
-	while (true) {
-	    int c = in.read();
+        while (true) {
+            if (termBracket >= 0 &&
+                    matchInput(in, BRACKET_CLOSE[termBracket])) {
+                throw new TemplateException(
+                        "Unexpected end of instruction", in);
+            }
+            
+            int c = in.read();
+            
+            if (c < 0)
+                throw new TemplateException("Unexpected end of file.", in);
+            
+            if (!fd.call)
+                out.write(c);
             
             if (isSeparator(c, LIST_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.SUBFUNCTION,
-				      in, out);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        fd.call ?
+                            DataFormat.SUBFUNCTION :
+                            DataFormat.SUBFUNCTION_NOCALL,
+                        in, out);
             } else if (isSeparator(c, REC_DATA_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.RECURSIVE,
-				      in, out);
-            } else if (isSeparator(c, NONREC_DATA_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.NONRECURSIVE, 
-				      in, out);
-            } else if (c < 0) {
-		throw new TemplateException("Unexpected end of file.", in);
+                skipEscaped(in);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        fd.call ?
+                            DataFormat.RECURSIVE :
+                            DataFormat.NOCALL,
+                        in, out);
+            } else if (isSeparator(c, VERBATIM_DATA_SEPARATORS)) {
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        DataFormat.VERBATIM, in, out);
+            } else if (isSeparator(c, NOCALL_DATA_SEPARATORS)) {
+                skipEscaped(in);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        DataFormat.NOCALL, in, out);
             } else {
-		sb.append((char)c);
-	    }
-	}
+                sb.append((char)c);
+            }
+        }
     }
-
-    int parseValue(Object value, Writer out) throws IOException {
+    
+    public int parseValue(Object value, Writer out) throws IOException {
         if (value == null)
             return 0;
-
+        
         String svalue = value.toString();
         
         if (svalue == null || svalue.length() == 0)
             return 0;
-
+        
         out.write(svalue);
         return 1;
     }
-
-    private int invokeFunction(String name, FunctionData fd,
-			       TemplateReader in, Writer out)
-	throws IOException, TemplateException {
-
-        FunctionDataParser fdp = new FunctionDataParser(this, fd, in);
-
-	if ("".equals(name)) {
-            return fdp.parseData(out);
-	}
+    
+    private int invoke(String name, DataFormat fd,
+            TemplateReader in, Writer out)
+            throws IOException, TemplateException {
         
-        Object value = getValue(name);
-        int r;
-
-        if (value instanceof Function) {            
-            r = ((Function) value).invoke(fdp, out);           
-        } else { 
-            r = parseValue(value, out);
+        FunctionDataParser fdp = new FunctionDataParser(this, fd, in);
+                
+        int r = invoke(name, fdp, out);                
+        
+        if (fdp.hasMoreData()) {
+            /* Skip remaining function data. */
+            fdp.skipData();
         }
-
-	if (fdp.hasMoreData()) {
-	    /* Skip remaining function data. */
-	    fdp.parseData(new Writer() {
-		    public void close() {}
-		    public void flush() {}
-		    public void write(char[] cbuf, int off, int len) {}
-		});
-	}
-	
-	return r;
+        
+        return r;
     }
-
-    public Object getValue(String name) throws TemplateException {
-	Object value = variables.get(name);
-	if (value != null) return value;   
-
-	if (superParser != null) {
-	    if (name.startsWith(SUPER_PREFIX))
-		return superParser.getValue
-                    (name.substring(SUPER_PREFIX.length()));
-	    else
-		return superParser.getValue(name);
-	}
-
-	return null;
-    }
-
-    public void setValue(String name, Object value) {
-	variables.put(name, value);
+          
+    public int invoke(String name, FunctionDataParser fdp, Writer out)
+    throws IOException, TemplateException {
+        Object value = context.get(name);
+        
+        if (value instanceof Function) {
+            return ((Function) value).invoke(fdp, out);
+        }
+        
+        if (value instanceof Context) {
+            String code = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            TemplateParser parser = new TemplateParser((Context) value);
+            return parser.parse(
+                    new TemplateReader(new StringReader(code)), out);
+        }
+        
+        return parseValue(value, out);
     }
-
-    public void clearValues() {
-	variables.clear();
-        Functions.registerAllFunctions(this);
-    }
-
-    // TEST
-    public static void main(String[] args)
-	throws IOException, TemplateException {
-	
-	TemplateParser p = new TemplateParser();
-	
-	TemplateReader in = 
-            new TemplateReader
-            (new LineNumberReader
-             (new FileReader("parser.in")), "parser.in");
-
-	FileWriter out = new FileWriter("parser.out");
-
-	p.parse(in, out);
-
-	in.close();
-	out.close();
+    
+    public Context getContext() {
+        return context;
     }
 }
--- a/src/kryshen/tema/TemplateReader.java	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  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
- *
- *  $Id: TemplateReader.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
- */
-
-package kryshen.tema;
-
-import java.io.Reader;
-import java.io.LineNumberReader;
-import java.io.FilterReader;
-import java.io.IOException;
-
-/**
- * Reader for TEMA templates. Stores data source name (commonly
- * filename) and tracks line numbers for error reporting.
- *
- * @author Mikhail A. Kryshen
- */
-public class TemplateReader extends FilterReader {
-    private String source = null;
-
-    private LineNumberReader lnReader = null;
-    private TemplateReader parentReader = null;
-
-    public TemplateReader(LineNumberReader in, String source) {
-        super(in);
-        this.lnReader = in;
-        this.source = source;
-    }
-
-    public TemplateReader(Reader in, TemplateReader parent) {
-        super(in);
-        this.source = source;
-    }
-
-    public String getSource() {
-        if (source != null)
-            return source;
-        
-        return parentReader.getSource();
-    }
-    
-    public int getLineNumber() {
-        if (lnReader != null)
-            return lnReader.getLineNumber();
-        
-        return parentReader.getLineNumber();
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/ant/TemaTask.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,145 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: TemaTask.java,v 1.17 2008/02/17 23:58:01 mikhail Exp $
+ */
+
+package kryshen.tema.ant;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Tema support for ant.
+ *
+ * @author Mikhail Kryshen
+ */
+public class TemaTask extends Task {
+    
+    public static class Variable {
+        private String name;
+        private String value;
+        
+        public void setName(String name) {
+            this.name = name;
+        }
+        
+        public void setValue(String value) {
+            this.value = value;
+        }
+        
+        public void setPath(File file) {
+            this.value = file.getAbsolutePath();
+        }
+    }
+    
+    private File basedir = null;
+    
+    private File infile = null;
+    private File outfile = null;
+    
+    private Collection<Variable> variables = new ArrayList<Variable>();
+    
+    @Override
+    public void execute() throws BuildException {
+        if (basedir == null) {
+            basedir = getProject().getBaseDir();
+        }
+        
+        if (infile == null) {
+            throw new BuildException("no infile specified", getLocation());
+        }
+        
+        try {
+            executeTema();
+        } catch (IOException e) {
+            throw new BuildException(e);
+        } catch (TemplateException e) {
+            throw new BuildException(e);
+        }
+    }
+    
+    private void executeTema() throws IOException, TemplateException {
+        TemplateParser parser = new TemplateParser(basedir);
+        
+        for (Variable var : variables) {
+            if (var.name == null) {
+                throw new BuildException(
+                        "no name specified for variable", getLocation());
+            }
+            
+            if (var.value == null) {
+                throw new BuildException(
+                        "no value specified for variable", getLocation());
+            }
+            
+            parser.getContext().set(var.name, var.value);
+        }
+        
+        TemplateReader in = new TemplateReader(
+                new BufferedReader(new FileReader(infile)),
+                infile.getName());
+        try {
+            Writer out;
+            
+            if (outfile != null) {
+                out = new BufferedWriter(new FileWriter(outfile));
+            } else {
+                out = NullWriter.INSTANCE;
+            }
+            
+            try {
+                parser.parse(in, out);
+            } finally {
+                out.close();
+            }
+        } finally {
+            in.close();
+        }
+    }
+    
+    public void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+    
+    public void setInfile(File infile) {
+        this.infile = infile;
+    }
+    
+    public void setOutfile(File outfile) {
+        this.outfile = outfile;
+    }
+    
+    public void addVariable(Variable variable) {
+        variables.add(variable);
+    }
+}
--- a/src/kryshen/tema/demo/DemoFrame.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/demo/DemoFrame.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,181 +1,228 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: DemoFrame.java,v 1.3 2006/12/14 15:59:48 mikhail Exp $
+ *  $Id: DemoFrame.java,v 1.13 2008/02/16 17:38:03 mikhail Exp $
  */
 
 package kryshen.tema.demo;
 
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
+import java.awt.Button;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.TextArea;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.net.URL;
-
-import kryshen.tema.*;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
 
 /**
- * TEMA demonstation console.
+ * Tema demonstation console.
  */
 public class DemoFrame extends Frame {
+    private static final Font TEXT_FONT =
+            new Font("monospaced", Font.PLAIN, 12);
+    
     private TextArea input;
     private TextArea output;
     private TextArea error;
     
+    private String sample;
+    
     public DemoFrame() {
-        super("TEMA demo console");
-
+        super("Tema demo console");
+        
         GridBagLayout gbl = new GridBagLayout();
-
+        
         GridBagConstraints c = new GridBagConstraints();
-
+        
         setLayout(gbl);
-
-        input = new TextArea(25, 40);
+        
+        input = new TextArea(28, 50);
         input.setEditable(true);
-
-        output = new TextArea(25, 40);
+        input.setFont(TEXT_FONT);
+        
+        output = new TextArea(28, 50);
         output.setEditable(false);
-
-        error = new TextArea(5, 80);
+        output.setFont(TEXT_FONT);
+        
+        error = new TextArea(5, 90);
         error.setEditable(false);
-
+        error.setFont(TEXT_FONT);
+        
         Label l;
-
+        
         c.insets = new Insets(3, 3, 3, 3);
         c.fill = GridBagConstraints.BOTH;
         c.gridwidth = GridBagConstraints.RELATIVE;
-
-        l = new Label("Enter your code and click \"Process\".");
-
+        
+        l = new Label("Enter your code and press \"Process\".");
+        
         gbl.setConstraints(l, c);
         add(l);
-
+        
         Panel buttons = new Panel();
         buttons.setLayout(new FlowLayout(FlowLayout.LEFT));
         
         Button process = new Button("Process");
         Button clear = new Button("Clear");
+        Button reset = new Button("Reset");
         Button close = new Button("Close");
-
+        
         buttons.add(process);
         buttons.add(clear);
+        buttons.add(reset);
         buttons.add(close);
-
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         gbl.setConstraints(buttons, c);
         add(buttons);
-
+        
         c.fill = GridBagConstraints.BOTH;
         c.anchor = GridBagConstraints.CENTER;
         c.gridwidth = GridBagConstraints.RELATIVE;
-
+        
         l = new Label("Input:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         l = new Label("Output:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
-        c.gridwidth = GridBagConstraints.RELATIVE; 
-
+        
+        c.gridwidth = GridBagConstraints.RELATIVE;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        
         gbl.setConstraints(input, c);
         add(input);
-       
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         gbl.setConstraints(output, c);
         add(output);
-
+        
+        c.weightx = 0.0;
+        c.weighty = 0.0;
+        
         l = new Label("Errors:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
+        
+        c.weightx = 1.0;
+        c.weighty = 0.25;
+        
         gbl.setConstraints(error, c);
-        add(error);      
-
+        add(error);
+        
         process.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    process();
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                process();
+            }
+        });
+        
         clear.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    error.setText("");
-                    output.setText("");
-                    input.setText("");
-                    
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                error.setText("");
+                output.setText("");
+                input.setText("");
+                input.requestFocus();
+            }
+        });
+        
+        reset.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                error.setText("");
+                output.setText("");
+                input.setText(sample);
+            }
+        });
+        
         close.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    dispose();
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                dispose();
+            }
+        });
+        
         addWindowListener(new WindowAdapter() {
-                public void windowClosing(WindowEvent e) {
-                    dispose();
-                }
-            });
+            public void windowClosing(WindowEvent e) {
+                dispose();
+            }
+        });
         
         URL source = getClass().getResource("demo.template");
         StringBuffer buffer = new StringBuffer();
-
+        
         try {
             Reader in = new BufferedReader
-                (new InputStreamReader(source.openStream(), "UTF-8"));           
+                    (new InputStreamReader(source.openStream(), "UTF-8"));
             
             for (int ch = in.read(); ch >= 0; ch = in.read()) {
                 buffer.append((char)ch);
             }
-
+            
             in.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         
-        input.setText(buffer.toString());
+        sample = buffer.toString();
+        
+        input.setText(sample);
     }
-
+    
     private void process() {
         String s = input.getText();
         
-        TemplateReader tr = new TemplateReader
-            (new LineNumberReader(new StringReader(s)), "input");
+        TemplateReader tr = new TemplateReader(new StringReader(s), "input");
         TemplateParser tp = new TemplateParser();
         StringWriter sw = new StringWriter();
-
+        
         error.setText("");
-
+        
         try {
             tp.parse(tr, sw);
         } catch (Exception e) {
             error.setText(e.toString());
+            e.printStackTrace();
         }
-
+        
         output.setText(sw.toString());
     }
 }
--- a/src/kryshen/tema/demo/Hello.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/demo/Hello.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,21 +1,23 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Hello.java,v 1.2 2006/12/14 14:39:24 mikhail Exp $
+ *  $Id: Hello.java,v 1.9 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema.demo;
@@ -27,7 +29,7 @@
 /**
  * Example function implementation.
  * 
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Hello extends Function {
     public Hello() {}
@@ -36,8 +38,6 @@
         throws IOException, TemplateException {
         
         out.write("Hello, ");
-        fdp.parseData(out);
-
-        return 1;
+        return fdp.parseData(out);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/functions/Control.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,166 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Control.java,v 1.18 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
+
+/**
+ * Logical and conditional functions.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Control {
+    
+    /**
+     * Copy input to output and return zero value.
+     */
+    public static final Function FALSE =
+            new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (fdp.hasMoreData())
+                fdp.parseData(out);
+            
+            return 0;
+        }
+    };
+
+    /**
+     * Copy input to output and return non-zero value.
+     */
+    public static final Function TRUE =
+            new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (fdp.hasMoreData())
+                fdp.parseData(out);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Outputs it's data if it has non-zero value.
+     */
+    public static final Function OPTIONAL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            StringWriter sw = new StringWriter();
+            int s = fdp.parseData(sw);
+            sw.close();
+            String data = sw.toString();
+            
+            if (s != 0) {
+                out.write(data);
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    };
+    
+    /**
+     * Logical negation (applied to the return value).
+     */
+    public static final Function NOT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            return (fdp.parseData(out) == 0) ? 1 : 0;
+        }
+    };
+    
+    public static final Function IF = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int value = fdp.parseNextArg(NullWriter.INSTANCE);
+            
+            if (value != 0) {
+                return fdp.parseData(out);
+            }
+            
+            return 0;
+        }
+    };
+    
+    public static final Function IF_ELSE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int value = fdp.parseNextArg(NullWriter.INSTANCE);
+            
+            if (value != 0) {
+                return fdp.parseNextArg(out);
+            } else {
+                fdp.skipNextArg();
+                return fdp.parseData(out);
+            }
+        }
+    };
+    
+    /**
+     * Outputs it's evaluated data while the evaluation result is non-zero.
+     */
+    public static final Function WHILE = new Function() {
+        public int invoke(FunctionDataParser fdp, final Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            StringReader dataReader = new StringReader(data);
+            
+            TemplateParser parser = fdp.getTemplateParser();
+            
+            int r = 0; // summarized return value
+            int e = 0; // evaluation value
+            
+            while (true) {
+                StringWriter sw = new StringWriter();
+                
+                e = parser.parse(new TemplateReader(dataReader), sw);
+                
+                if (e == 0)
+                    break;
+                
+                r += Math.abs(e);
+                out.write(sw.toString());
+                dataReader.reset();
+            }
+            
+            dataReader.close();
+            return r;
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/functions/Database.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,208 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Database.java,v 1.12 2008/02/17 23:58:01 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
+
+/**
+ * Data access functions.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Database {   
+    /* db_connect:connection_name resource */
+    public static final Function CONNECT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            Connection connection;
+            
+            try {
+                connection = DriverManager.getConnection(data);
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(arg0, connection);
+            
+            out.write(arg0);
+            return 1;
+        }
+    };
+    
+    /* db_prepare:qury_name connection sql_statement */
+    public static final Function PREPARE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg(); // resulting variable
+            String arg1 = fdp.getNextArg(); // connection
+            String data = fdp.getData();    // statement
+            
+            Connection connection;
+            PreparedStatement statement;
+            
+            try {
+                connection = (Connection) fdp.getContext().get(arg1);
+                statement = connection.prepareStatement(data);
+            } catch (ClassCastException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(arg0, statement);
+            
+            out.write(arg0);
+            return 1;
+        }
+    };
+    
+    /* db_query:sql_query template arg1 arg2 ... argN */
+    public static final Function QUERY = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String query = fdp.getNextArg();
+            String template = fdp.getNextArg();
+            List<String> args = fdp.getArgs();
+            
+            TemplateParser tp = fdp.getTemplateParser();
+            Context context = fdp.getContext();
+            
+            try {
+                PreparedStatement statement =
+                        (PreparedStatement) context.get(query);
+                return query(template, fdp.getTemplateReader(), 
+                        statement, args, tp, out);
+            } catch (ClassCastException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+        }
+    };
+    
+    /**
+     * Process database query.
+     *
+     * @param template template to fill with data.
+     * @param parentReader parent TemplateReader.
+     * @param ps query statement to execute.
+     * @param args list of query parameters.
+     * @param superParser invoking object.
+     * @param out Writer to output processed data.
+     *
+     * @return number of processed rows in query result.
+     */
+    private static int query(String template,
+            TemplateReader parentReader,
+            PreparedStatement ps,
+            List<String> args,
+            TemplateParser superParser,
+            Writer out)
+            throws IOException, SQLException, TemplateException {
+        
+        int i = 1;
+        
+        for (String arg : args) {
+            ps.setString(i++, arg);
+        }
+        
+        ResultSet resultSet = ps.executeQuery();
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        int columnCount = metaData.getColumnCount();
+        
+        String[] names = new String[columnCount];
+        
+        for (int j = 0; j < columnCount; j++) {
+            names[j] = metaData.getColumnName(j + 1);
+        }
+        
+        final TemplateParser p = new TemplateParser(superParser);
+        final HashMap<String, Object> fields = new HashMap<String, Object>();
+        
+        p.getContext().set("db", new Function() {
+            public int invoke(FunctionDataParser fdp,
+                    final Writer out)
+                    throws IOException, TemplateException {
+                
+                String name = fdp.getData();
+                Object value = fields.get(name);
+                
+                return p.parseValue(value, out);
+            }
+        });
+        
+        Reader input = new StringReader(template);
+        
+        for (i = 1; resultSet.next(); i++) {
+            
+            for (int j = 0; j < columnCount; j++) {
+                Object value = resultSet.getObject(j + 1);
+                if (resultSet.wasNull()) value = null;
+                
+                fields.put(names[j], value);
+            }
+            
+            p.getContext().set("db_row", i);
+            
+            TemplateReader tr = new TemplateReader(input, parentReader);
+            
+            p.parse(tr, out);
+
+            input.reset();            
+            fields.clear();
+        }
+        
+        resultSet.close();
+        input.close();
+        ps.clearParameters();
+        return i - 1;
+    }
+}
--- a/src/kryshen/tema/functions/Define.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/functions/Define.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,84 +1,126 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Define.java,v 1.7 2006/12/14 14:35:00 mikhail Exp $
+ *  $Id: Define.java,v 1.25 2008/02/17 23:58:01 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * Define function implementation.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Define extends Function {
     public static final Function DEFINE = new Define();
-
+    
     public Define() {
     }
-
+    
     public int invoke(FunctionDataParser fdp, Writer out)
-        throws IOException, TemplateException {
+    throws IOException, TemplateException {
+        
+        StringWriter nameWriter = new StringWriter();
+        int r = fdp.parseNextArg(nameWriter);
+        
+        if (r == 0)
+            return 0;                
         
-        final String arg0 = fdp.getNextArg();
-        final String data = fdp.getData();
-
+        StringWriter dataWriter = new StringWriter();
+        r = fdp.parseData(dataWriter);
+        
+        if (r == 0)
+            return 0;
+               
+        final String name = nameWriter.toString();
+        final String data = dataWriter.toString();
+        
         Function newFunction = new Function() {
-                public int invoke(final FunctionDataParser fdp, final Writer out)
-                    throws IOException, TemplateException {
-		    
-                    TemplateParser functionParser = 
-                        new TemplateParser(fdp.getTemplateParser());
-                    
-                    functionParser.registerFunction
-			("nextarg", new Function() {
-				public int invoke(FunctionDataParser unusedFdp, 
-						  final Writer out)
-				    throws IOException, TemplateException {
-				    
-				    return fdp.parseNextArg(out);
-				}
-			    });
-		    
-                    functionParser.registerFunction
-			("data",new Function() {
-				public int invoke(FunctionDataParser unusedFdp, 
-						  final Writer out)
-				    throws IOException, TemplateException {
-				    
-				    return fdp.parseData(out);
-				}
-			    });
-		    
-                    TemplateReader dataReader = new TemplateReader
-                        (new StringReader(data), fdp.getTemplateReader());
-
-                    return functionParser.parse(dataReader, out);
-                }
-            };
-	
-        fdp.getTemplateParser().registerFunction(arg0, newFunction);
-	
-        out.write(arg0);
-        return 1;
+            public int invoke(final FunctionDataParser fdp1, final Writer out)
+            throws IOException, TemplateException {
+                
+                final TemplateParser functionParser =
+                        new TemplateParser(fdp1.getTemplateParser());
+                
+                final Context functionContext = 
+                        functionParser.getContext();                
+                
+                functionContext.set
+                        ("next_arg", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        if (fdp1.hasMoreData())
+                            return fdp1.parseNextArg(out);
+                        
+                        return 0;
+                    }
+                });
+                
+                functionContext.set
+                        ("data", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        return fdp1.parseData(out);
+                    }
+                });
+                
+                functionContext.set
+                        ("has_more_data", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        if (fdp1.hasMoreData()) {
+                            out.write("true");
+                            return 1;
+                        }
+                        
+                        out.write("false");
+                        return 0;
+                    }
+                });
+                
+                TemplateReader dataReader = new TemplateReader
+                        (new StringReader(data), fdp1.getTemplateReader());
+                
+                return functionParser.parse(dataReader, out);
+            }
+        };
+        
+        fdp.getContext().set(name, newFunction);
+        
+        out.write(name);
+        return r;
     }
 }
--- a/src/kryshen/tema/functions/IO.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/functions/IO.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,133 +1,168 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: IO.java,v 1.4 2006/12/14 15:59:49 mikhail Exp $
+ *  $Id: IO.java,v 1.19 2008/02/19 16:21:00 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.Tema;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * I/O functions.
  */
 public class IO {
-    public static final Function COPY =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-
-                File source = new File
-                    (Tema.getProperty("resource_base"), arg0);
-                File dest = new File(arg1);
-                
-                try {
-                    copyFile(source, dest);
-                } catch (IOException e) {
-                    System.err.println(e);
-                    return 0;
-                }
-                out.write(arg1);
-                return 1;
+    /**
+     * Copy files.
+     */
+    public static final Function COPY = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String arg1 = fdp.getNextArg();
+            
+            File source = fdp.createFile(arg0);
+            File dest = fdp.createFile(arg1);
+            
+            try {
+                copyFile(source, dest);
+            } catch (IOException e) {
+                System.err.println(e);
+                return 0;
+            }
+            out.write(arg1);
+            return 1;
+        }
+    };
+    
+    /**
+     * Write data to file.
+     */
+    public static final Function WRITE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            
+            System.err.println("Writing " + arg0 + "...");
+            
+            Writer fw;
+            
+            try {
+                fw = Tema.createFileWriter(fdp.createFile(arg0));
+            } catch (IOException e) {
+                System.err.println(e);
+                //throw new TemplateException(e.getMessage(), e, in);
+                return 0;
+            }
+            
+            try {
+                fdp.parseData(fw);
+            } finally {
+                fw.close();
             }
-        };
-
-    public static final Function WRITE =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-
-		System.err.println("Writing " + arg0 + "...");
-
-                Writer fw;
-
-                try {
-		    fw = Tema.createFileWriter(arg0);
-                } catch (IOException e) {
-                    System.err.println(e);
-                    //throw new TemplateException(e.getMessage(), e, in);
-                    return 0;
-                }
-
-                try {
-		    fdp.parseData(fw);
-                } finally {
-                    fw.close();
-                }
-
-                out.write(arg0);
-
-		System.err.println("Saved " + arg0 + ".");
-                return 1;
+            
+            out.write(arg0);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Read from file.
+     */
+    public static final Function READ = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String filename = fdp.getData();
+            String file;
+            
+            try {
+                readFile(fdp.createFile(filename), out);
+            } catch (IOException e) {
+                System.err.println(e);
+                //throw new TemplateException(e.getMessage(), e, in);
+                return 0;
             }
-        };
-
-    public static final Function READ =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-	       String filename = fdp.getData();
-	       String file;
-	       
-                try {
-                    file = Tema.readFile(new File(filename));
-                } catch (IOException e) {
-                    System.err.println(e);
-                    //throw new TemplateException(e.getMessage(), e, in);
-                    return 0;
-                }
-		
-                out.write(file);
-                return 1;
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Evaluate the code read from file.
+     */
+    public static final Function INCLUDE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String filename = fdp.getData();
+            
+            TemplateReader fin;
+            
+            try {
+                fin = Tema.createTemplateReader(fdp.createFile(filename));
+            } catch (IOException e) {
+                throw new TemplateException(e.getMessage(), e,
+                        fdp.getTemplateReader());
             }
-        };
-
-    public static final Function INCLUDE =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-                
-                String filename = fdp.getData();
-                
-                TemplateReader fin;
-
-                try {
-                    fin = Tema.createTemplateReader(new File(filename));
-                } catch (IOException e) {
-                    throw new TemplateException(e.getMessage(), e,
-                                                fdp.getTemplateReader());
-                }
-
-                int r = fdp.getTemplateParser().parse(fin, out);
-
-                fin.close();
-                return r;
-            }
-        };
-
+            
+            int r = fdp.getTemplateParser().parse(fin, out);
+            
+            fin.close();
+            return r;
+        }
+    };
+    
+    /**
+     * Create path from base path and file name.
+     */
+    public static final Function FILE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String base = fdp.getNextArg();
+            String name = fdp.getNextArg();                        
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;                        
+                       
+            out.write((new File(fdp.createFile(base), name)).getPath());
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
     /**
      * Update file (copy if newer).
      *
@@ -137,27 +172,51 @@
      * @trows IOException on copying error.
      */
     private static void copyFile(File src, File dest) throws IOException {
-	System.err.print("Copying " + src + "... ");
-	
-	if (src.lastModified() < dest.lastModified()) {
-	    System.err.println(dest + " is up to date.");
-	    return;
-	}
-
-	File parent = dest.getParentFile();
-	if (parent != null) parent.mkdirs();
-
+        System.err.print("Copying " + src + "... ");
+        
+        if (src.lastModified() < dest.lastModified()) {
+            System.err.println(dest + " is up to date.");
+            return;
+        }
+        
+        File parent = dest.getParentFile();
+        if (parent != null) parent.mkdirs();
+        
         InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dest);
         
+        copy(in, out);
+        
+        in.close();
+        out.close();
+    }
+    
+    private static void copy(InputStream in, OutputStream out)
+    throws IOException {
+        
         byte[] buffer = new byte[1024];
         int len;
+        
         while ((len = in.read(buffer)) > 0) {
             out.write(buffer, 0, len);
         }
-        in.close();
-        out.close();
-
-	System.err.println("saved " + dest + ".");
+    }
+    
+    private static void copy(Reader in, Writer out)
+    throws IOException {
+        
+        char[] buffer = new char[1024];
+        int len;
+        
+        while ((len = in.read(buffer)) > 0) {
+            out.write(buffer, 0, len);
+        }
+    }
+    
+    private static void readFile(File src, Writer out)
+    throws IOException {
+        
+        Reader in = Tema.createFileReader(src);
+        copy(in, out);
     }
 }
--- a/src/kryshen/tema/functions/ImageConverter.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/functions/ImageConverter.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,21 +1,23 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: ImageConverter.java,v 1.4 2006/12/14 19:44:32 mikhail Exp $
+ *  $Id: ImageConverter.java,v 1.13 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema.functions;
@@ -33,11 +35,11 @@
 /**
  * Convert images to specified format.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class ImageConverter extends Function {    
 
-    public static final Function IMAGE = new ImageConverter();
+    public static final Function CONVERT_IMAGE = new ImageConverter();
 
     public int invoke(FunctionDataParser fdp, Writer out)
         throws IOException, TemplateException {
@@ -49,14 +51,11 @@
             Integer.parseInt(fdp.getNextArg()) : -1;
         int arg4 = fdp.hasMoreData() ? 
             Integer.parseInt(fdp.getNextArg()) : -1;
-        
-        File source = new File
-            (Tema.getProperty("resource_base"), arg0);
-        
+
         try {
             convert
-                (source,                  /* source file */
-                 new File(arg1),          /* dest file */
+                (fdp.createFile(arg0),    /* source file */
+                 fdp.createFile(arg1),    /* dest file */
                  arg2,                    /* format */
                  arg3,                    /* max width */
                  arg4);                   /* max height */
--- a/src/kryshen/tema/functions/Logics.java	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  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
- *
- *  $Id: Logics.java,v 1.6 2006/12/14 14:39:26 mikhail Exp $
- */
-
-package kryshen.tema.functions;
-
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
-
-/**
- * Logical and conditional functions.
- *
- * @author Mikhail A. Kryshen
- */
-public class Logics {
-    public static final Function FALSE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-		
-		fdp.parseData(out);
-                return 0;
-            }
-        };
-
-    public static final Function TRUE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-		
-		fdp.parseData(out);
-                return 1;
-            }
-        };
-
-    /**
-     * Outputs it's data it has non-zero value.
-     */
-    public static final Function OPTIONAL =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                StringWriter sw = new StringWriter();
-                int s = fdp.parseData(sw);
-                sw.close();
-                String data = sw.toString();
-
-                if (s != 0) {
-                    out.write(data);
-                    return 1;
-                } else {
-                    return 0;
-                }
-            }
-        };
-}
--- a/src/kryshen/tema/functions/ReplaceWriter.java	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  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
- *
- *  $Id: ReplaceWriter.java,v 1.2 2006/12/14 14:39:26 mikhail Exp $
- */
-
-package kryshen.tema.functions;
-
-import java.io.Writer;
-import java.io.FilterWriter;
-import java.io.IOException;
-
-import java.util.LinkedList;
-import java.util.Iterator;
-
-/**
- * FilterWriter which replaces characters with escape strings.
- *
- * @author Mikhail A. Kryshen
- */
-class ReplaceWriter extends FilterWriter {
-    private String[] patterns;
-    private String[] replaces;
-
-    private String prefix;
-    private String postfix;
-
-    private int maxPatternLength = 0;
-
-    /** Have this Writer processed any data? */
-    private boolean processedData = false;
-
-    private StringBuffer buffer = new StringBuffer();
-
-    public ReplaceWriter(Writer w, String pattern, String replace) {
-        this(w, new String[]{pattern}, new String[]{replace});
-    }
-
-    public ReplaceWriter(Writer w, String pattern, String replace,
-                         String prefix, String postfix) {
-
-        this(w, new String[]{pattern}, new String[]{replace}, 
-             prefix, postfix);
-    }
-
-    public ReplaceWriter(Writer w, String[] patterns, String[] replaces) {
-        this(w, patterns, replaces, null, null);
-    }
-
-    public ReplaceWriter(Writer w, String[] patterns, String[] replaces, 
-                         String prefix, String postfix) {
-	super(w);
-        
-	this.patterns = patterns;
-	this.replaces = replaces;
-        
-        this.prefix = prefix;
-        this.postfix = postfix;
-        
-	for (int i = 0; i < patterns.length; i++) {
-	    int length = patterns[i].length();
-	    if (length > maxPatternLength)
-		maxPatternLength = length;
-	}
-    }
-
-    private boolean processBuffer(int minLength) throws IOException {
-        if (buffer.length() == 0) return false;
-
-        if (!processedData) {
-            if (prefix != null)
-                super.write(prefix, 0, prefix.length());
-            processedData = true;
-        }
-
-        int k;
-	for (k = 0; minLength + k < buffer.length(); k++) {
-	    for (int i = 0; i < patterns.length; i++) {
-		String pattern = patterns[i];
-		int length = pattern.length();
-
-                if (length + k > buffer.length())
-                    continue;
-
-		boolean match = true;
-
-		for (int j = 0; j < length; j++) {
-		    if (pattern.charAt(j) != buffer.charAt(j + k)) {
-			match = false;
-                        break;
-		    }
-		}
-
-		if (match) {
-                    super.write(buffer.substring(0, k), 0, k);
-                    buffer.delete(0, k + length);
-                    super.write(replaces[i], 0, replaces[i].length());
-		    return true;
-		}
-	    }
-	}
-
-        super.write(buffer.substring(0, k), 0, k);
-        buffer.delete(0, k);
-
-        return false;
-    }
-
-    public void write(int c) throws IOException {
-	buffer.append((char)c);
-	processBuffer(maxPatternLength);
-    }
-
-    public void write(char[] cbuf, int off, int len) throws IOException {
-	for (int i = off; i < off + len; i++) {
-	    buffer.append(cbuf[i]);
-	}
-	processBuffer(maxPatternLength);
-    }
-
-    public void write(String str, int off, int len) throws IOException {
-        buffer.append(str.substring(off, off + len));
-	processBuffer(maxPatternLength);
-    }
-
-    public void finish() throws IOException {
-        while (processBuffer(0));
-
-//         super.write(buffer.toString(), 0, buffer.length());
-//         buffer.delete(0, buffer.length());
-
-        if (processedData && postfix != null)
-            super.write(postfix, 0, postfix.length());
-    }
-
-    public void close() throws IOException {
-        finish();
-        super.close();
-    }
-}
--- a/src/kryshen/tema/functions/Standard.java	Thu Dec 14 23:22:05 2006 +0300
+++ b/src/kryshen/tema/functions/Standard.java	Tue Feb 19 20:32:17 2008 +0300
@@ -1,258 +1,259 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
  *
- *  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.
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is distributed in the hope that 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.
+ *  GNU Lesser 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
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Standard.java,v 1.5 2006/12/14 14:39:26 mikhail Exp $
+ *  $Id: Standard.java,v 1.34 2008/02/19 17:32:17 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-import java.net.*;
-
-import java.sql.SQLException;
-import java.sql.PreparedStatement;
-
-import kryshen.tema.*;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.Tema;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.NullWriter;
 
 /**
  * Standard TEMA functions.
  */
 public class Standard {
-    public static final Function SET =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getData();
-                
-                fdp.getTemplateParser().setValue(arg0, arg1);
+    
+    /**
+     * Output Tema version.
+     */
+    public static final Function TEMA = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            out.write(Tema.TITLE + " " + Tema.VERSION
+                    /*+ " by " + Tema.AUTHOR*/);
+            return 1;
+        }
+    };
+    
+    /**
+     * Define static variable.
+     */
+    public static final Function SET = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            String data = fdp.getData(out);
+            fdp.getContext().set(name, data);
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    /**
+     * Recursively remove definition.
+     */
+    public static final Function UNSET = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int r = 0;
+            
+            while (fdp.hasMoreData()) {
+                String name = fdp.getNextArg();
                 
-		out.write(arg0);
-                return 1;
-            }
-        };
-
-    /* prepare:qury_name sql_statement */
-    public static final Function PREPARE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String data = fdp.getData();
-
-		PreparedStatement statement;
-
-		try {
-		    statement = Tema.getDbConnection()
-                        .prepareStatement(data);
-		} catch (SQLException e) {
-		    throw new TemplateException
-                        (e.getMessage(), e, fdp.getTemplateReader());
-		}
+                if (fdp.getLastReturnCode() == 0)
+                    continue;
                 
-                fdp.getTemplateParser().setValue(arg0, statement);   
-             
-		out.write(arg0);
-                return 1;
-            }
-        };
-
-    /* query:sql_query template arg1 arg2 ... argN */
-    public static final Function QUERY =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-                
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-		List<String> args = fdp.getArgs();				
-
-		TemplateParser tp = fdp.getTemplateParser();
-
-                TemplateReader tr = 
-                    new TemplateReader(new StringReader(arg1),
-                                       fdp.getTemplateReader());
-
-		try {
-		    return Tema.query
-			(tr, (PreparedStatement)tp.getValue(arg0),
-			 args, tp, out);
-		} catch (SQLException e) {
-		    throw new TemplateException
-                        (e.getMessage(), e, fdp.getTemplateReader());
-		}
+                if (fdp.getContext().unset(name))
+                    r++;
             }
-        };
-
-    public static final Function REPLACE =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-
-                ReplaceWriter rw = new ReplaceWriter(out, arg0, arg1);
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
-            }
-        };
-
-    public static final Function LOAD =
-
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
+            
+            return r;
+        }
+    };
+    
+    /**
+     * Export variable to the global (outermost) context.
+     */
+    public static final Function EXPORT = new Function() {
+        public int invoke(FunctionDataParser fdp,
+                final Writer out)
                 throws IOException, TemplateException {
-                
-                final String name = fdp.getNextArg();
-                final String className = fdp.getNextArg();
-
-                List<URL> urls = new ArrayList<URL>(1);
-
-                while (fdp.hasMoreData()) {
-                    urls.add(new URL(fdp.getNextArg()));                    
-                }
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            // Set and export.
+            if (fdp.hasMoreData()) {
+                Object value = fdp.getData(out);
                 
-                ClassLoader loader = this.getClass().getClassLoader();
-
-                if (urls.size() > 0) {
-                    loader = new URLClassLoader
-                        (urls.toArray(new URL[0]), loader);
-                }                
-
-                Class<Function> functionClass;
-                Function function;
-
-                try {
-                    functionClass = loadFunctionClass(loader, className);
-                } catch (ClassNotFoundException e) {
-                    throw new TemplateException
-                        ("Class not found", e, fdp.getTemplateReader());
-                } catch (ClassCastException e) {
-                    throw new TemplateException
-                        ("Not a function class", e, fdp.getTemplateReader());
-                }
-                
-                try {
-                    function = functionClass.newInstance();
-                } catch (InstantiationException e) {
-                    throw new TemplateException
-                        ("Could not load class", e, fdp.getTemplateReader());
-                } catch (IllegalAccessException e) {
-                    throw new TemplateException
-                        ("Could not load class", e, fdp.getTemplateReader());
-                }
-
-                fdp.getTemplateParser().registerFunction(name, function);
-                                
-
+                fdp.getContext().export(name, value);
+                return fdp.getLastReturnCode();
+            }
+            
+            // Only export.
+            if (fdp.getContext().export(name)) {
                 out.write(name);
                 return 1;
             }
-        };
-
-    public static final Function NULL_OUTPUT =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
+            
+            return 0;
+        }
+    };
+    
+    /**
+     * Copy macro definitions.
+     */
+    public static final Function ASSIGN = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+                throws IOException, TemplateException {
 
-		/* Write nothing. */
-		return fdp.parseData(new Writer() {
-                    public void close() {}
-                    public void flush() {}
-                    public void write(char[] cbuf, int off, int len) {}
-                });
-            }
-        };
+            String name1 = fdp.getNextArg();
 
-    public static final Function XML_ESCAPE =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            String name2 = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
 
-                final String[] chars = {"&", "<", ">", "`", "\""};
-                final String[] escape = {"&amp;", "&lt;", "&gt;", "&apos;", "&quot;"};
-
-                ReplaceWriter rw = new ReplaceWriter(out, chars, escape);
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
-            }
-        };
-
-    public static final Function XML_CDATA =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
-
-                ReplaceWriter rw = new ReplaceWriter
-                    (out, "]]>", "]]]><![CDATA[]>", "<![CDATA[", "]]>");
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
+            Context context = fdp.getContext();
+            Object value = context.get(name2);
+            
+            if (value == null)
+                return 0;
+            
+            context.set(name1, value);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Invoke function specified by name.
+     */
+    public static final Function INVOKE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            return fdp.getTemplateParser().invoke(name, fdp, out);            
+        }
+    };
+    
+    /**
+     * Create definiton as a new Java object.
+     */
+    public static final Function LOAD = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            final String name = fdp.getNextArg();
+            final String className = fdp.getNextArg();
+            
+            List<URL> urls = new ArrayList<URL>(1);
+            
+            while (fdp.hasMoreData()) {
+                urls.add(new URL(fdp.getNextArg()));
             }
-        };
-
-
-    @SuppressWarnings("unchecked")
-    private static Class<Function> loadFunctionClass
-        (ClassLoader loader, String className)
-        throws ClassNotFoundException {
-        
-        return (Class<Function>)loader.loadClass(className);
-    }
-
+            
+            ClassLoader loader = this.getClass().getClassLoader();
+            
+            if (urls.size() > 0) {
+                loader = new URLClassLoader
+                        (urls.toArray(new URL[0]), loader);
+            }
+            
+            Class<?> loadedClass;
+            Object instance;
+            
+            try {
+                loadedClass = loader.loadClass(className);
+            } catch (ClassNotFoundException e) {
+                throw new TemplateException
+                        ("Class not found", e, fdp.getTemplateReader());
+            }
+            
+            try {
+                instance = loadedClass.newInstance();
+            } catch (InstantiationException e) {
+                throw new TemplateException
+                        ("Could not load class", e, fdp.getTemplateReader());
+            } catch (IllegalAccessException e) {
+                throw new TemplateException
+                        ("Could not load class", e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(name, instance);
+                        
+            out.write(name);
+            return 1;
+        }
+    };
+    
+    public static final Function ECHO = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            return fdp.parseData(out);
+        }
+    };
+    
     /**
-     * Update file (copy if newer).
-     *
-     * @param src source file.
-     * @param dest destination file.
-     *
-     * @trows IOException on copying error.
+     * Process arguments, but output nothing.
      */
-    private static void copyFile(File src, File dest) throws IOException {
-	System.err.print("Copying " + src + "... ");
-	
-	if (src.lastModified() < dest.lastModified()) {
-	    System.err.println(dest + " is up to date.");
-	    return;
-	}
-
-	File parent = dest.getParentFile();
-	if (parent != null) parent.mkdirs();
-
-        InputStream in = new FileInputStream(src);
-        OutputStream out = new FileOutputStream(dest);
-        
-        byte[] buffer = new byte[1024];
-        int len;
-        while ((len = in.read(buffer)) > 0) {
-            out.write(buffer, 0, len);
+    public static final Function SILENT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            return fdp.parseData(NullWriter.INSTANCE);
         }
-        in.close();
-        out.close();
-
-	System.err.println("saved " + dest + ".");
-    }
+    };
+    
+    /**
+     * Skip arguments.
+     */
+    public static final Function SKIP = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            // Ignore arguments.
+            return 0;
+        }
+    };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/functions/Strings.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,225 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Strings.java,v 1.13 2008/02/16 17:38:03 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.ReplaceWriter;
+
+/**
+ * Functions for string manipulation.
+ */
+public class Strings {
+    public static final Function TO_UPPER = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            out.write(data.toUpperCase());
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function TO_LOWER = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            out.write(data.toLowerCase());
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function SUBSTRING = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String is = fdp.getNextArg();
+            String js = fdp.getNextArg();
+            
+            String data = fdp.getData();
+            
+            try {
+                if (js.equals("$")) {
+                    out.write(data.substring(Integer.parseInt(is)));
+                } else {
+                    out.write(data.substring(
+                            Integer.parseInt(is),
+                            Integer.parseInt(js)));
+                }
+            } catch (NumberFormatException e) {
+                throw new TemplateException(
+                        e.toString(), e, fdp.getTemplateReader());
+            } catch (IndexOutOfBoundsException e) {
+                throw new TemplateException(
+                        e.toString(), e, fdp.getTemplateReader());
+            }
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function REPLACE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String arg1 = fdp.getNextArg();
+            
+            ReplaceWriter rw = new ReplaceWriter(out, arg0, arg1);
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function XML_ESCAPE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            final String[] chars = {"&", "<", ">", "`", "\""};
+            final String[] escape = {"&amp;", "&lt;", "&gt;", "&apos;", "&quot;"};
+            
+            ReplaceWriter rw = new ReplaceWriter(out, chars, escape);
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function XML_CDATA = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            ReplaceWriter rw = new ReplaceWriter
+                    (out, "]]>", "]]]><![CDATA[]>", "<![CDATA[", "]]>");
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function REGEX_MATCH = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();            
+            String data = fdp.getData(out);
+            
+            if (data.matches(regex)) {
+                return 1;
+            }
+            
+            return 0;
+        }
+    };
+    
+    public static final Function REGEX_REPLACE_FIRST = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();
+            String replacement = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            out.write(data.replaceFirst(regex, replacement));
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function REGEX_REPLACE_ALL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();
+            String replacement = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            out.write(data.replaceAll(regex, replacement));
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function EQUAL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String s1 = fdp.getNextArg();
+            
+            while (fdp.hasMoreData()) {
+                String s2 = fdp.getNextArg();
+                
+                if (!s1.equals(s2)) {
+                    return 0;
+                }
+                
+                s1 = s2;
+            }
+
+            return 1;
+        }
+    };
+    
+    public static final Function CHAR = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+                throws IOException, TemplateException {           
+
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            do {
+                String s = fdp.getNextArg();
+                int code;
+
+                try {
+                    if (s.startsWith("0x")) {
+                        code = Integer.parseInt(s.substring(2), 16);
+                    } else {
+                        code = Integer.parseInt(s);
+                    }
+                } catch (NumberFormatException e) {
+                    throw new TemplateException(e.getMessage(), e,
+                            fdp.getTemplateReader());
+                }
+
+                out.write(code);                
+            } while (fdp.hasMoreData());
+                
+            return 1;
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/io/CopyWriter.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: CopyWriter.java,v 1.7 2008/02/16 17:56:34 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Copies it's output to several writers.
+ *
+ * @author Mikhail Kryshen
+ */
+public class CopyWriter extends Writer {
+    private final Writer[] writers;
+    
+    public CopyWriter(Writer... writers) {
+        this.writers = writers;
+    }
+    
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        for (Writer w : writers) {
+            w.write(cbuf, off, len);
+        }
+    }
+    
+    public void write(int c) throws IOException {
+        for (Writer w : writers) {
+            w.write(c);
+        }
+    }
+    
+    public void write(String str, int off, int len) throws IOException {
+        for (Writer w : writers) {
+            w.write(str, off, len);
+        }
+    }
+    
+    public void flush() throws IOException {
+        for (Writer w : writers) {
+            w.flush();
+        }
+    }
+    
+    public void close() throws IOException {
+        for (Writer w : writers) {
+            w.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/io/NullWriter.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,46 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: NullWriter.java,v 1.6 2008/02/16 17:38:03 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Writer;
+
+/**
+ * No-op writer.
+ *
+ * @author Mikhail Kryshen
+ */
+public class NullWriter extends Writer{
+    public static final NullWriter INSTANCE = new NullWriter();
+    
+    private NullWriter() {        
+    }
+    
+    public void close() {
+    }
+    
+    public void flush() {
+    }
+    
+    public void write(char[] cbuf, int off, int len) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/io/ReplaceWriter.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,153 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: ReplaceWriter.java,v 1.8 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Writer;
+import java.io.FilterWriter;
+import java.io.IOException;
+
+/**
+ * FilterWriter which replaces characters with escape strings.
+ *
+ * @author Mikhail Kryshen
+ */
+public class ReplaceWriter extends FilterWriter {
+    private String[] patterns;
+    private String[] replaces;
+
+    private String prefix;
+    private String postfix;
+
+    private int maxPatternLength = 0;
+
+    /** Have this Writer processed any data? */
+    private boolean processedData = false;
+
+    private StringBuffer buffer = new StringBuffer();
+
+    public ReplaceWriter(Writer w, String pattern, String replace) {
+        this(w, new String[]{pattern}, new String[]{replace});
+    }
+
+    public ReplaceWriter(Writer w, String pattern, String replace,
+                         String prefix, String postfix) {
+
+        this(w, new String[]{pattern}, new String[]{replace}, 
+             prefix, postfix);
+    }
+
+    public ReplaceWriter(Writer w, String[] patterns, String[] replaces) {
+        this(w, patterns, replaces, null, null);
+    }
+
+    public ReplaceWriter(Writer w, String[] patterns, String[] replaces, 
+                         String prefix, String postfix) {
+	super(w);
+        
+	this.patterns = patterns;
+	this.replaces = replaces;
+        
+        this.prefix = prefix;
+        this.postfix = postfix;
+        
+	for (int i = 0; i < patterns.length; i++) {
+	    int length = patterns[i].length();
+	    if (length > maxPatternLength)
+		maxPatternLength = length;
+	}
+    }
+
+    private boolean processBuffer(int minLength) throws IOException {
+        if (buffer.length() == 0) return false;
+
+        if (!processedData) {
+            if (prefix != null)
+                super.write(prefix, 0, prefix.length());
+            processedData = true;
+        }
+
+        int k;
+	for (k = 0; minLength + k < buffer.length(); k++) {
+	    for (int i = 0; i < patterns.length; i++) {
+		String pattern = patterns[i];
+		int length = pattern.length();
+
+                if (length + k > buffer.length())
+                    continue;
+
+		boolean match = true;
+
+		for (int j = 0; j < length; j++) {
+		    if (pattern.charAt(j) != buffer.charAt(j + k)) {
+			match = false;
+                        break;
+		    }
+		}
+
+		if (match) {
+                    super.write(buffer.substring(0, k), 0, k);
+                    buffer.delete(0, k + length);
+                    super.write(replaces[i], 0, replaces[i].length());
+		    return true;
+		}
+	    }
+	}
+
+        super.write(buffer.substring(0, k), 0, k);
+        buffer.delete(0, k);
+
+        return false;
+    }
+
+    public void write(int c) throws IOException {
+	buffer.append((char)c);
+	processBuffer(maxPatternLength);
+    }
+
+    public void write(char[] cbuf, int off, int len) throws IOException {
+	for (int i = off; i < off + len; i++) {
+	    buffer.append(cbuf[i]);
+	}
+	processBuffer(maxPatternLength);
+    }
+
+    public void write(String str, int off, int len) throws IOException {
+        buffer.append(str.substring(off, off + len));
+	processBuffer(maxPatternLength);
+    }
+
+    public void finish() throws IOException {
+        while (processBuffer(0));
+
+//         super.write(buffer.toString(), 0, buffer.length());
+//         buffer.delete(0, buffer.length());
+
+        if (processedData && postfix != null)
+            super.write(postfix, 0, postfix.length());
+    }
+
+    public void close() throws IOException {
+        finish();
+        super.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kryshen/tema/io/TemplateReader.java	Tue Feb 19 20:32:17 2008 +0300
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: TemplateReader.java,v 1.8 2008/02/19 00:20:48 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Reader;
+import java.io.LineNumberReader;
+import java.io.PushbackReader;
+import kryshen.tema.TemplateParser;
+
+/**
+ * Reader for Tema templates. Stores data source name (commonly
+ * filename) and tracks line numbers for error reporting.
+ *
+ * @author Mikhail Kryshen
+ */
+public class TemplateReader extends PushbackReader {
+    static final int UNREAD_BUFFER_SIZE;
+    
+    /* Calculate UNREAD_BUFFER_SIZE value. */
+    static {
+        int max = 0;
+        
+        for (String s : TemplateParser.BRACKET_OPEN) {
+            max = Math.max(max, s.length());
+        }
+        
+        for (String s : TemplateParser.BRACKET_CLOSE) {
+            max = Math.max(max, s.length());
+        }
+        
+        max = Math.max(max, TemplateParser.ESCAPE_NEWLINE.length() + 2);
+        max = Math.max(max, TemplateParser.ESCAPE_WHITESPACE.length());
+        
+        UNREAD_BUFFER_SIZE = max;
+    }
+    
+    private final String source;
+    private final LineNumberReader lnReader;
+    private final TemplateReader parentReader;
+    
+    public TemplateReader(Reader in) {
+        this(new LineNumberReader(in));
+    }
+    
+    public TemplateReader(Reader in, String source) {
+        this(new LineNumberReader(in), source);
+    }
+    
+    public TemplateReader(LineNumberReader in) {
+        this(in, "");
+    }
+    
+    public TemplateReader(LineNumberReader in, String source) {
+        super(in, UNREAD_BUFFER_SIZE);
+        
+        this.parentReader = null;
+        this.lnReader = in;
+        this.source = source;
+    }
+    
+    public TemplateReader(Reader in, TemplateReader parent) {
+        super(in, UNREAD_BUFFER_SIZE);
+        
+        this.parentReader = parent;
+        this.lnReader = null;
+        this.source = null;
+    }
+    
+    public String getSource() {
+        if (source != null)
+            return source;
+        
+        return parentReader.getSource();
+    }
+    
+    public int getLineNumber() {
+        if (lnReader != null)
+            return lnReader.getLineNumber();
+        
+        return parentReader.getLineNumber();
+    }
+}
--- a/test/demo	Thu Dec 14 23:22:05 2006 +0300
+++ b/test/demo	Tue Feb 19 20:32:17 2008 +0300
@@ -1,4 +1,4 @@
 #!/bin/sh
 
 #java -jar ../dist/tema.jar
-java -classpath .:../dist/tema.jar kryshen.tema.Tema
+java -classpath .:../dist/tema.jar kryshen.tema.Tema main.template
--- a/test/demo.bat	Thu Dec 14 23:22:05 2006 +0300
+++ b/test/demo.bat	Tue Feb 19 20:32:17 2008 +0300
@@ -1,1 +1,1 @@
-java -classpath .:..\dist\tema.jar kryshen.tema.Tema
+java -classpath .:..\dist\tema.jar kryshen.tema.Tema main.template
--- a/test/include.template	Thu Dec 14 23:22:05 2006 +0300
+++ b/test/include.template	Tue Feb 19 20:32:17 2008 +0300
@@ -1,1 +1,1 @@
-<%define\test test arg1:[%nextarg:%], test arg2:[%nextarg:%], test data:[%data:%].%>
\ No newline at end of file
+<%define#test test arg1:<%next_arg:%>, test arg2:<%next_arg:%>, test data:<%data:%>.%>
\ No newline at end of file
--- a/test/main.template	Thu Dec 14 23:22:05 2006 +0300
+++ b/test/main.template	Tue Feb 19 20:32:17 2008 +0300
@@ -1,4 +1,4 @@
-[%!\ UTF-8 text %]
+[%!\ UTF-8 text %]\
 
 Вывод текста:
 test text
@@ -10,7 +10,7 @@
 <%include:include.template%>
 
 Вызов новой функции:
-<%test:1 2 3%>
+<%test:1 2 3 4 5%>
 
 Тестирование read:
 <%read\include.template%>
--- a/test/tema.properties	Thu Dec 14 23:22:05 2006 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# Data source configuration
-# resource          : jdbc:odbc:database
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-# resource_base     : .
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-input_encoding    : UTF-8
-output_encoding   : UTF-8
-
-# Cache templates
-# cache_read        : true
-
-# Output main_template parsing result to stderr
-# output            : stderr
-
-# File to output error messages (redirect stderr)
-# log               : dbreader.log